[
  {
    "path": ".gitignore",
    "content": "# MAC OS\n.DS_Store\n\n#\nbuild/\nbin/\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: cpp\ncompiler:\n  - clang\nscript: make\nbranches:\n  only:\n    - master\nnotifications:\n  recipients:\n    - bertyoung666@gmail.com\n  email:\n    on_success: change\n    on_failure: always\nos:\n  - osx\n"
  },
  {
    "path": "CMakeCommon",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nIF(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n    SET(CMAKE_CXX_FLAGS_DEBUG  \"-g -Wall -std=c++0x\")\n    SET(CMAKE_CXX_FLAGS_RELEASE \"-O2 -g -Wall -std=c++0x\")\nELSEIF(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n    SET(CMAKE_CXX_FLAGS_DEBUG  \"-g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions\")\n    SET(CMAKE_CXX_FLAGS_RELEASE \"-O2 -g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions\")\nELSE()\n    message(FATAL_ERROR \"Only support linux or OS X, support for windows is in plan\")\nENDIF()\n\nOPTION(DEBUG \"Debug or release\" OFF)\n\nIF(DEBUG)\n    SET(CMAKE_BUILD_TYPE \"Debug\")\nELSE()\n    SET(CMAKE_BUILD_TYPE \"Release\")\nENDIF()\n\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nPROJECT(QEDIS)\n\nSUBDIRS(QBase)\nSUBDIRS(QedisCore)\nSUBDIRS(QedisSvr)\nSUBDIRS(Modules)\nSUBDIRS(UnitTest)\n\n\nSET(QEDIS_CLUSTER 0)\nSET(USE_ZOOKEEPER 0)\n\nADD_DEFINITIONS(-DQEDIS_CLUSTER=${QEDIS_CLUSTER})\nADD_DEFINITIONS(-DUSE_ZOOKEEPER=${USE_ZOOKEEPER})\nif(${QEDIS_CLUSTER} EQUAL 1)\n    SUBDIRS(QSentinel)\nendif()\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Bert Young\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 all\ncopies 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 THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "all:\n\t@if [ ! -d \"leveldb\" ]; then echo \"leveldb not present. Fetching leveldb-1.18 from internet...\"; curl -s -L -O https://github.com/google/leveldb/archive/v1.18.tar.gz; tar xzvf v1.18.tar.gz; rm -f v1.18.tar.gz; mv leveldb-1.18 leveldb; fi\n\tcd leveldb && make && cd -\n\tmkdir -p bin && ln -s -f ../leveldb/libleveldb.so.1.18 bin/libleveldb.so.1\n\tmkdir -p build && cd build && cmake .. && make \n\nclean:\n\t@if [ -d \"build\" ]; then cd build && make clean && cd -; fi\ncleanall:clean\n\t@if [ -d \"leveldb\" ]; then cd leveldb && make clean && cd -; rm -rf leveldb; fi\n"
  },
  {
    "path": "Modules/CMakeLists.txt",
    "content": "INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nAUX_SOURCE_DIRECTORY(. QEDIS_MODULE_SRC)\n\nLINK_DIRECTORIES(../../leveldb)\n\nSET(LIBRARY_OUTPUT_PATH ../../bin)\nSET(QEDIS_MODULE qedismodule)\n\nADD_LIBRARY(${QEDIS_MODULE} SHARED ${QEDIS_MODULE_SRC})\n\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase)\n\nADD_DEPENDENCIES(${QEDIS_MODULE} qediscore leveldb)\n# clang needs below, not necessary for gcc\nTARGET_LINK_LIBRARIES(${QEDIS_MODULE} qbaselib; qediscore; leveldb)\n\nSET_TARGET_PROPERTIES(${QEDIS_MODULE} PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "Modules/QHashModule.cc",
    "content": "#include \"QHashModule.h\"\n#include \"QHash.h\"\n#include \"QStore.h\"\n#include \"QGlobRegex.h\"\n\nusing namespace qedis;\n\nQError hgets(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_hash);\n    if (err != QError_ok) \n    {  \n        ReplyError(err, reply);\n        return err;\n    }\n    \n    const PHASH& hash= value->CastHash();\n    std::vector<const QString* > res;\n    for (const auto& kv : *hash)\n    {\n        if (glob_match(params[2], kv.first))\n        {\n            res.push_back(&kv.first);\n            res.push_back(&kv.second);\n        }\n    }\n\n    PreFormatMultiBulk(res.size(), reply);\n    for (auto v : res)\n    {\n        FormatBulk(*v, reply);\n    }\n\n    return   QError_ok;\n}\n\n"
  },
  {
    "path": "Modules/QHashModule.h",
    "content": "#ifndef BERT_QHASHMODULE_H\n#define BERT_QHASHMODULE_H\n\n#include <vector>\n#include \"QString.h\"\n#include \"QCommon.h\"\n\nnamespace qedis\n{\nclass UnboundedBuffer;\n}\n\n\n// hgets  hash_key  filter_pattern\n// for example, `hgets profile c*y` may return [\"city\":\"shanghai\", \"country\":\"china\"]\n// because c*y matches \"city\" and \"country\"\n//\nextern \"C\"\nqedis::QError hgets(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\n#endif\n\n"
  },
  {
    "path": "Modules/QListModule.cc",
    "content": "#include \"QListModule.h\"\n#include \"QList.h\"\n#include \"QStore.h\"\n#include <algorithm>\n\nusing namespace qedis;\n\n\nQError ldel(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return err;\n    }\n        \n    long idx;\n    if (!Strtol(params[2].c_str(), params[2].size(), &idx))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n        \n    const PLIST& list = value->CastList();\n    const int size = static_cast<int>(list->size());\n    if (idx < 0)\n        idx += size;\n    \n    if (idx < 0 || idx >= size)\n    {\n        Format0(reply);\n        return QError_nop;\n    }\n    \n    if (2 * idx < size)\n    {\n        auto it = list->begin();\n        std::advance(it, idx);\n        list->erase(it);\n    }\n    else\n    {\n        auto it = list->rbegin();\n        idx = size - 1 - idx;\n        std::advance(it, idx);\n        list->erase((++it).base());\n    }\n\n    if (list->empty())\n        QSTORE.DeleteKey(params[1]);\n    \n    Format1(reply);\n    return QError_ok;\n}\n\n"
  },
  {
    "path": "Modules/QListModule.h",
    "content": "#ifndef BERT_QLISTMODULE_H\n#define BERT_QLISTMODULE_H\n\n#include <vector>\n#include \"QString.h\"\n#include \"QCommon.h\"\n\nnamespace qedis\n{\nclass UnboundedBuffer;\n}\n\n\n// ldel  list_key  item_index\n// For delete list item by index\n//\nextern \"C\"\nqedis::QError ldel(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\n#endif\n\n"
  },
  {
    "path": "Modules/QModuleInit.cc",
    "content": "#include \"QModuleInit.h\"\n#include \"QCommand.h\"\n#include <algorithm>\n\nextern \"C\"\nqedis::QError ldel(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\nextern \"C\"\nqedis::QError hgets(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\nextern \"C\"\nqedis::QError skeys(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\nbool QedisModule_OnLoad()\n{\n    printf(\"enter %s\\n\", __FUNCTION__);\n    using namespace qedis;\n\n    // register list ldel command\n    static QCommandInfo ldelinfo;\n    ldelinfo.cmd = \"ldel\";\n    ldelinfo.attr = QAttr_write;\n    ldelinfo.params = 3;\n    ldelinfo.handler = &ldel;\n\n    if (!QCommandTable::AddCommand(\"ldel\", &ldelinfo))\n        return false;\n\n    // register hash hgets command\n    static QCommandInfo hgetsinfo;\n    hgetsinfo.cmd = \"hgets\";\n    hgetsinfo.attr = QAttr_read;\n    hgetsinfo.params = 3;\n    hgetsinfo.handler = &hgets;\n\n    if (!QCommandTable::AddCommand(\"hgets\", &hgetsinfo))\n    {\n        QCommandTable::DelCommand(\"ldel\");\n        return false;\n    }\n\n    // register set skeys command\n    static QCommandInfo skeysinfo;\n    skeysinfo.cmd = \"skeys\";\n    skeysinfo.attr = QAttr_read;\n    skeysinfo.params = 3;\n    skeysinfo.handler = &skeys;\n\n    if (!QCommandTable::AddCommand(\"skeys\", &skeysinfo))\n    {\n        QCommandTable::DelCommand(\"hgets\");\n        QCommandTable::DelCommand(\"ldel\");\n        return false;\n    }\n    \n    printf(\"exit %s\\n\", __FUNCTION__);\n    return true;\n}\n\n\nvoid QedisModule_OnUnLoad()\n{\n    printf(\"enter %s\\n\", __FUNCTION__);\n    qedis::QCommandTable::DelCommand(\"skeys\");\n    qedis::QCommandTable::DelCommand(\"hgets\");\n    qedis::QCommandTable::DelCommand(\"ldel\");\n    printf(\"exit %s\\n\", __FUNCTION__);\n}\n"
  },
  {
    "path": "Modules/QModuleInit.h",
    "content": "#ifndef BERT_QMODULEINIT_H\n#define BERT_QMODULEINIT_H\n\nextern \"C\"\nbool QedisModule_OnLoad();\n\nextern \"C\"\nvoid QedisModule_OnUnLoad();\n#endif\n\n"
  },
  {
    "path": "Modules/QSetModule.cc",
    "content": "#include \"QSetModule.h\"\n#include \"QSet.h\"\n#include \"QStore.h\"\n#include \"QGlobRegex.h\"\n\nusing namespace qedis;\n\nQError skeys(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_set);\n    if (err != QError_ok) \n    {  \n        if (err == QError_notExist)\n            FormatNull(reply);\n        else\n            ReplyError(err, reply);\n\n        return err;\n    }\n\n    std::vector<const QString* > res;\n    const PSET& set = value->CastSet();\n    for (const auto& k : *set)\n    {\n        if (glob_match(params[2], k))\n        {\n            res.push_back(&k);\n        }\n    }\n\n    PreFormatMultiBulk(res.size(), reply);\n    for (auto v : res)\n    {\n        FormatBulk(*v, reply);\n    }\n\n    return   QError_ok;\n}\n\n\n"
  },
  {
    "path": "Modules/QSetModule.h",
    "content": "#ifndef BERT_QSETMODULE_H\n#define BERT_QSETMODULE_H\n\n#include <vector>\n#include \"QString.h\"\n#include \"QCommon.h\"\n\nnamespace qedis\n{\nclass UnboundedBuffer;\n}\n\n\n// skeys set_key  filter_pattern\n// for example, `skeys cities sh*` may return [\"shanghai\", \"shenzhen\"]\n//\nextern \"C\"\nqedis::QError skeys(const std::vector<qedis::QString>& params, qedis::UnboundedBuffer* reply);\n\n#endif\n\n"
  },
  {
    "path": "QBase/AsyncBuffer.cc",
    "content": "#include <assert.h>\n\n#if defined(__APPLE__)\n#include <unistd.h>\n#endif\n\n#include \"AsyncBuffer.h\"\n\nusing std::size_t;\n\nAsyncBuffer::AsyncBuffer(size_t size) : buffer_(size),\n                                          backBytes_(0)\n{\n}\n\nAsyncBuffer::~AsyncBuffer()\n{\n  //  assert (buffer_.IsEmpty());\n   // assert (backBytes_ == 0);\n}\n\n\nvoid   AsyncBuffer::Write(const void* data, size_t len)\n{\n    BufferSequence  bf;\n    bf.buffers[0].iov_base = const_cast<void* >(data);\n    bf.buffers[0].iov_len  = len;\n    bf.count = 1;\n    \n    this->Write(bf);\n}\n\nvoid   AsyncBuffer::Write(const BufferSequence& data)\n{\n    auto len = data.TotalBytes();\n\n    if (backBytes_ > 0 || buffer_.WritableSize() < len)\n    {\n        std::lock_guard<std::mutex>  guard(backBufLock_);\n\n        if (backBytes_ > 0 || buffer_.WritableSize() < len)\n        {\n            for (size_t i = 0; i < data.count; ++ i)\n            {\n                backBuf_.PushData(data.buffers[i].iov_base,\n                                   data.buffers[i].iov_len);\n            }\n        \n            backBytes_ += len;\n            assert (backBytes_ == backBuf_.ReadableSize());\n\n            return;\n        }\n    }\n    \n    assert(backBytes_ == 0 && buffer_.WritableSize() >= len);\n\n    for (size_t i = 0; i < data.count; ++ i)\n    {\n        buffer_.PushData(data.buffers[i].iov_base, data.buffers[i].iov_len);\n    }\n}\n\nvoid  AsyncBuffer::ProcessBuffer(BufferSequence& data)\n{\n    data.count = 0;\n    \n    // Here be dragons! see below...\n    if (!tmpBuf_.IsEmpty())\n    {\n        data.count = 1;\n        data.buffers[0].iov_base = tmpBuf_.ReadAddr();\n        data.buffers[0].iov_len  = tmpBuf_.ReadableSize();\n    }\n    else if (!buffer_.IsEmpty())\n    {\n        auto nLen = buffer_.ReadableSize();\n\n        buffer_.GetDatum(data, nLen);\n        assert (nLen == data.TotalBytes());\n    }\n    else\n    {\n        if (backBytes_ > 0 && backBufLock_.try_lock())\n        {\n            // tmpBuf_ is used for process backBuf_ without held mutex!\n            backBytes_ = 0;\n            tmpBuf_.Swap(backBuf_);\n            backBufLock_.unlock();\n            \n            data.count = 1;\n            data.buffers[0].iov_base = tmpBuf_.ReadAddr();\n            data.buffers[0].iov_len  = tmpBuf_.ReadableSize();\n        }\n    }\n}\n\nvoid  AsyncBuffer::Skip(size_t  size)\n{\n    if (!tmpBuf_.IsEmpty())\n    {\n        assert(size <= tmpBuf_.ReadableSize());\n        tmpBuf_.AdjustReadPtr(size);\n    }\n    else\n    {\n        assert(buffer_.ReadableSize() >= size);\n        buffer_.AdjustReadPtr(size);\n    }\n}\n\n"
  },
  {
    "path": "QBase/AsyncBuffer.h",
    "content": "#ifndef BERT_ASYNCBUFFER_H\n#define BERT_ASYNCBUFFER_H\n\n#include <mutex>\n#include <atomic>\n\n#include \"Buffer.h\"\n#include \"UnboundedBuffer.h\"\n\nclass AsyncBuffer\n{\npublic:\n    explicit\n    AsyncBuffer(std::size_t  size = 128 * 1024);\n   ~AsyncBuffer();\n\n    void        Write(const void* data, std::size_t len);\n    void        Write(const BufferSequence& data);\n\n    void        ProcessBuffer(BufferSequence& data);\n    void        Skip(std::size_t  size);\n\nprivate:\n    // for async write\n    Buffer          buffer_;\n    \n    // double buffer\n    qedis::UnboundedBuffer tmpBuf_;\n    \n    std::mutex      backBufLock_;\n    std::atomic<std::size_t>    backBytes_;\n    qedis::UnboundedBuffer backBuf_;\n};\n\n#endif\n\n"
  },
  {
    "path": "QBase/Buffer.h",
    "content": "\n#ifndef BERT_BUFFER_H\n#define BERT_BUFFER_H\n\n#include <cassert>\n#include <cstring>\n#include <vector>\n#include <string>\n#include <sys/uio.h>\n#include <atomic>\n\n\nstruct BufferSequence\n{\n    static const std::size_t kMaxIovec = 16;\n\n    iovec       buffers[kMaxIovec];\n    std::size_t count;\n\n    std::size_t TotalBytes() const\n    {\n        assert (count <= kMaxIovec);\n        std::size_t nBytes = 0;\n        for (std::size_t i = 0; i < count; ++ i)\n            nBytes += buffers[i].iov_len;\n\n        return nBytes;\n    }\n};\n\n\n//static const std::size_t  DEFAULT_BUFFER_SIZE = 4 * 1024;\n\ninline std::size_t RoundUp2Power(std::size_t size)\n{\n    if (0 == size)  return 0;\n\n    std::size_t roundSize = 1;\n    while (roundSize < size)\n        roundSize <<= 1;\n\n    return roundSize;\n}\n\n\ntemplate <typename BUFFER>\nclass  CircularBuffer\n{\npublic:\n    // Constructor  to be specialized\n    explicit CircularBuffer(std::size_t size = 0) : maxSize_(size),\n    readPos_(0),\n    writePos_(0)\n    {\n    }\n    CircularBuffer(const BufferSequence& bf);\n    CircularBuffer(char* , std::size_t );\n   ~CircularBuffer() { }\n\n    bool IsEmpty() const { return writePos_ == readPos_; }\n    bool IsFull()  const { return ((writePos_ + 1) & (maxSize_ - 1)) == readPos_; }\n\n    // For gather write\n    void GetDatum(BufferSequence& buffer, std::size_t maxSize, std::size_t offset = 0);\n\n    // For scatter read\n    void GetSpace(BufferSequence& buffer, std::size_t offset = 0);\n\n    // Put data into internal buffer_\n    bool PushData(const void* pData, std::size_t nSize);\n    bool PushDataAt(const void* pData, std::size_t nSize, std::size_t offset = 0);\n\n    // Get data from internal buffer_, adjust read ptr\n    bool PeekData(void* pBuf, std::size_t nSize);\n    bool PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset = 0);\n\n    char* ReadAddr() { return &buffer_[readPos_]; }\n    char* WriteAddr() { return &buffer_[writePos_]; }\n\n    void AdjustWritePtr(std::size_t size);\n    void AdjustReadPtr(std::size_t size);\n\n    std::size_t ReadableSize()  const {  return (writePos_ - readPos_) & (maxSize_ - 1);  }\n    std::size_t WritableSize()  const {  return maxSize_ - ReadableSize() - 1;  }\n\n    std::size_t Capacity() const { return maxSize_; }\n    void InitCapacity(std::size_t size);\n\n    template <typename T>\n    CircularBuffer& operator<< (const T& data);\n    template <typename T>\n    CircularBuffer& operator>> (T& data);\n\n    template <typename T>\n    CircularBuffer & operator<< (const std::vector<T>& );\n    template <typename T>\n    CircularBuffer & operator>> (std::vector<T>& );\n\n    CircularBuffer & operator<< (const std::string& str);\n    CircularBuffer & operator>> (std::string& str);\n\nprotected:\n    // The max capacity of buffer_\n    std::size_t maxSize_;\n\nprivate:\n    // The starting address can be read\n    std::atomic<std::size_t> readPos_;\n\n    // The starting address can be write\n    std::atomic<std::size_t> writePos_;\n\n    // The real internal buffer\n    BUFFER buffer_;\n\n    bool   owned_ = false;\n};\n\ntemplate <typename BUFFER>\nvoid CircularBuffer<BUFFER>::GetDatum(BufferSequence& buffer, std::size_t maxSize, std::size_t offset)\n{\n    if (maxSize == 0 ||\n        offset >= ReadableSize()\n       )\n    {\n        buffer.count = 0;\n        return;\n    }\n\n    assert(readPos_  < maxSize_);\n    assert(writePos_ < maxSize_);\n\n    std::size_t   bufferIndex  = 0;\n    const std::size_t readPos  = (readPos_ + offset) & (maxSize_ - 1);\n    const std::size_t writePos = writePos_;\n    assert (readPos != writePos);\n\n    buffer.buffers[bufferIndex].iov_base = &buffer_[readPos];\n    if (readPos < writePos)\n    {            \n        if (maxSize < writePos - readPos)\n            buffer.buffers[bufferIndex].iov_len = maxSize;\n        else\n            buffer.buffers[bufferIndex].iov_len = writePos - readPos;\n    }\n    else\n    {\n        std::size_t nLeft = maxSize;\n        if (nLeft > (maxSize_ - readPos))\n            nLeft = (maxSize_ - readPos);\n        buffer.buffers[bufferIndex].iov_len = nLeft;\n        nLeft = maxSize - nLeft;\n \n        if (nLeft > 0 && writePos > 0)\n        {\n            if (nLeft > writePos)\n                nLeft = writePos;\n\n            ++ bufferIndex;\n            buffer.buffers[bufferIndex].iov_base = &buffer_[0];\n            buffer.buffers[bufferIndex].iov_len = nLeft;\n        }\n    }\n\n    buffer.count = bufferIndex + 1;\n}\n\ntemplate <typename BUFFER>\nvoid CircularBuffer<BUFFER>::GetSpace(BufferSequence& buffer, std::size_t offset)\n{\n    assert(readPos_ >= 0 && readPos_ < maxSize_);\n    assert(writePos_ >= 0 && writePos_ < maxSize_);\n\n    if (WritableSize() <= offset + 1)\n    {\n        buffer.count = 0;\n        return;\n    }\n\n    std::size_t bufferIndex = 0;\n    const std::size_t readPos  = readPos_;\n    const std::size_t writePos = (writePos_ + offset) & (maxSize_ - 1);\n\n    buffer.buffers[bufferIndex].iov_base = &buffer_[writePos];\n\n    if (readPos > writePos)\n    {\n        buffer.buffers[bufferIndex].iov_len = readPos - writePos - 1;\n        assert (buffer.buffers[bufferIndex].iov_len > 0);\n    }\n    else\n    {\n        buffer.buffers[bufferIndex].iov_len = maxSize_ - writePos;\n        if (0 == readPos)\n        {\n            buffer.buffers[bufferIndex].iov_len -= 1;\n        }\n        else if (readPos > 1)\n        {\n            ++ bufferIndex;\n            buffer.buffers[bufferIndex].iov_base = &buffer_[0];\n            buffer.buffers[bufferIndex].iov_len = readPos - 1;\n        }\n    }\n\n    buffer.count = bufferIndex + 1;\n}\n\ntemplate <typename BUFFER>\nbool CircularBuffer<BUFFER>::PushDataAt(const void* pData, std::size_t nSize, std::size_t offset)\n{\n    if (!pData || 0 == nSize)\n        return true;\n\n    if (offset + nSize > WritableSize())\n        return false;\n\n    const std::size_t readPos = readPos_;\n    const std::size_t writePos = (writePos_ + offset) & (maxSize_ - 1);\n    if (readPos > writePos)\n    {\n        assert(readPos - writePos > nSize);\n        ::memcpy(&buffer_[writePos], pData, nSize);\n    }\n    else\n    {\n        std::size_t availBytes1 = maxSize_ - writePos;\n        std::size_t availBytes2 = readPos - 0;\n        assert (availBytes1 + availBytes2 >= 1 + nSize);\n\n        if (availBytes1 >= nSize + 1)\n        {\n            ::memcpy(&buffer_[writePos], pData, nSize);\n        }\n        else\n        {\n            ::memcpy(&buffer_[writePos], pData, availBytes1);\n            int bytesLeft = static_cast<int>(nSize - availBytes1);\n            if (bytesLeft > 0)\n                ::memcpy(&buffer_[0], static_cast<const char*>(pData) + availBytes1, bytesLeft);\n        }\n    }\n\n    return  true;\n}\n\ntemplate <typename BUFFER>\nbool CircularBuffer<BUFFER>::PushData(const void* pData, std::size_t nSize)\n{\n    if (!PushDataAt(pData, nSize))\n        return false;\n\n    AdjustWritePtr(nSize);\n    return true;\n}\n\ntemplate <typename BUFFER>\nbool CircularBuffer<BUFFER>::PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset)\n{\n    if (!pBuf || 0 == nSize)\n        return true;\n\n    if (nSize + offset > ReadableSize())\n        return false;\n\n    const std::size_t writePos = writePos_;\n    const std::size_t readPos  = (readPos_ + offset) & (maxSize_ - 1);\n    if (readPos < writePos)\n    {\n        assert(writePos - readPos >= nSize);\n        ::memcpy(pBuf, &buffer_[readPos], nSize);\n    }\n    else\n    {\n        assert(readPos > writePos);\n        std::size_t availBytes1 = maxSize_ - readPos;\n        std::size_t availBytes2 = writePos - 0;\n        assert(availBytes1 + availBytes2 >= nSize);\n\n        if (availBytes1 >= nSize)\n        {\n            ::memcpy(pBuf, &buffer_[readPos], nSize);            \n        }\n        else\n        {\n            ::memcpy(pBuf, &buffer_[readPos], availBytes1);\n            assert(nSize - availBytes1 > 0);\n            ::memcpy(static_cast<char*>(pBuf) + availBytes1, &buffer_[0], nSize - availBytes1);\n        }\n    }\n\n    return true;\n}\n\ntemplate <typename BUFFER>\nbool CircularBuffer<BUFFER>::PeekData(void* pBuf, std::size_t nSize)\n{\n    if (PeekDataAt(pBuf, nSize))\n        AdjustReadPtr(nSize);\n    else \n        return false;\n\n    return true;\n}\n\n\ntemplate <typename BUFFER>\ninline void CircularBuffer<BUFFER>::AdjustWritePtr(std::size_t size)\n{\n    std::size_t writePos = writePos_;\n    writePos += size;\n    writePos &= maxSize_ - 1;\n    \n    writePos_ = writePos;\n}\n\ntemplate <typename BUFFER>\ninline void CircularBuffer<BUFFER>::AdjustReadPtr(std::size_t size)\n{\n    std::size_t readPos = readPos_;\n    readPos += size;\n    readPos &= maxSize_ - 1;\n    \n    readPos_ = readPos;\n}\n\ntemplate <typename BUFFER>\ninline void CircularBuffer<BUFFER>::InitCapacity(std::size_t size)\n{\n    assert (size > 0 && size <= 1 * 1024 * 1024 * 1024);\n\n    maxSize_ = RoundUp2Power(size);\n    buffer_.resize(maxSize_);\n    std::vector<char>(buffer_).swap(buffer_);\n}\n\ntemplate <typename BUFFER>\ntemplate <typename T>\ninline CircularBuffer<BUFFER>& CircularBuffer<BUFFER>::operator<< (const T& data )\n{\n    if (!PushData(&data, sizeof(data)))\n        assert (!!!\"Please modify the DEFAULT_BUFFER_SIZE\");\n\n    return *this;\n}\n\ntemplate <typename BUFFER>\ntemplate <typename T>\ninline CircularBuffer<BUFFER> & CircularBuffer<BUFFER>::operator>> (T& data )\n{\n    if (!PeekData(&data, sizeof(data)))\n        assert(!!!\"Not enough data in buffer_\");\n\n    return *this;\n}\n\ntemplate <typename BUFFER>\ntemplate <typename T>\ninline CircularBuffer<BUFFER> & CircularBuffer<BUFFER>::operator<< (const std::vector<T>& v)\n{\n    if (!v.empty())\n    {\n        (*this) << static_cast<unsigned short>(v.size());\n        for ( typename std::vector<T>::const_iterator it = v.begin(); it != v.end(); ++it )\n        {\n            (*this) << *it;\n        } \n    }\n    return *this;\n}\n\ntemplate <typename BUFFER>\ntemplate <typename T>\ninline CircularBuffer<BUFFER> & CircularBuffer<BUFFER>::operator>> (std::vector<T>& v)\n{\n    v.clear();\n    unsigned short size;\n    *this >> size;\n    v.reserve(size);\n\n    while (size--)\n    {\n        T t;\n        *this >> t;\n        v.push_back(t);\n    }\n    return *this;\n}\n\ntemplate <typename BUFFER>\ninline CircularBuffer<BUFFER>& CircularBuffer<BUFFER>::operator<< (const std::string& str)\n{\n    *this << static_cast<unsigned short>(str.size());\n    if (!PushData(str.data(), str.size()))\n    {\n        AdjustWritePtr(static_cast<int>(0 - sizeof(unsigned short)));\n        assert(!!!\"2Please modify the DEFAULT_BUFFER_SIZE\");\n    }\n    return *this;\n}\n\ntemplate <typename BUFFER>\ninline CircularBuffer<BUFFER>& CircularBuffer<BUFFER>::operator>> (std::string& str)\n{\n    unsigned short size = 0;\n    *this >> size;\n    str.clear();\n    str.reserve(size);\n\n    char ch;\n    while ( size-- )\n    {\n        *this >> ch;\n        str += ch;\n    }\n    return *this;\n}\n\n///////////////////////////////////////////////////////////////\ntypedef CircularBuffer< ::std::vector<char> >  Buffer;\n\ntemplate <>\ninline Buffer::CircularBuffer(std::size_t maxSize) : maxSize_(RoundUp2Power(maxSize)), readPos_(0), writePos_(0), buffer_(maxSize_)\n{\n    assert (0 == (maxSize_ & (maxSize_ - 1)) && \"maxSize_ MUST BE power of 2\");\n}\n\n\ntemplate <int N>\nclass StackBuffer : public CircularBuffer<char [N]>\n{\n    using CircularBuffer<char [N]>::maxSize_;\npublic:\n    StackBuffer()\n    {\n        maxSize_ = N;\n        if (maxSize_ < 0)\n            maxSize_ = 1;\n\n        if (0 != (maxSize_ & (maxSize_ - 1)))\n            maxSize_ = RoundUp2Power(maxSize_);\n\n        assert (0 == (maxSize_ & (maxSize_ - 1)) && \"maxSize_ MUST BE power of 2\");\n    }\n};\n\ntypedef CircularBuffer<char* > AttachedBuffer;\n\ntemplate <>\ninline AttachedBuffer::CircularBuffer(char* pBuf, std::size_t len) :\n    maxSize_(RoundUp2Power(len + 1)),\n    readPos_(0),\n    writePos_(len)\n{\n    buffer_ = pBuf;\n    owned_  = false;\n}\n\ntemplate <>\ninline AttachedBuffer::CircularBuffer(const BufferSequence& bf) :\nreadPos_(0),\nwritePos_(0)\n{\n    owned_ = false;\n\n    if (0 == bf.count)\n    {\n        buffer_ = 0;\n    }\n    else if (1 == bf.count)\n    {\n        buffer_   = (char*)bf.buffers[0].iov_base;\n        writePos_ = static_cast<int>(bf.buffers[0].iov_len);\n    }\n    else if (bf.count > 1)\n    {\n        owned_  = true;\n        buffer_ = new char[bf.TotalBytes()];\n\n        std::size_t off = 0;\n        for (std::size_t i = 0; i < bf.count; ++ i)\n        {\n            memcpy(buffer_ + off, bf.buffers[i].iov_base, bf.buffers[i].iov_len);\n            off += bf.buffers[i].iov_len;\n        }\n\n        writePos_ = bf.TotalBytes();\n    }\n\n    maxSize_ = RoundUp2Power(writePos_ - readPos_ + 1);\n}\n\ntemplate <>\ninline AttachedBuffer::~CircularBuffer()\n{\n    if (owned_)\n        delete [] buffer_;\n}\n\ntemplate <typename T>\ninline void OverwriteAt(void* addr, T data)\n{\n\tmemcpy(addr, &data, sizeof(data));\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nAUX_SOURCE_DIRECTORY(. SDK_SRC)\nAUX_SOURCE_DIRECTORY(./Log     SDK_SRC)\nAUX_SOURCE_DIRECTORY(./Threads SDK_SRC)\nAUX_SOURCE_DIRECTORY(./SmartPtr SDK_SRC)\nAUX_SOURCE_DIRECTORY(./lzf SDK_SRC)\nSET(LIBRARY_OUTPUT_PATH ../../bin)\nADD_LIBRARY(qbaselib SHARED ${SDK_SRC})\n\nTARGET_LINK_LIBRARIES(qbaselib; pthread)\n\nSET_TARGET_PROPERTIES(qbaselib PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "QBase/ClientSocket.cc",
    "content": "\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <cstdlib>\n#include <cstring>\n#include <cassert>\n#include \"Server.h\"\n#include \"ClientSocket.h\"\n#include \"Log/Logger.h\"\n#include \"NetThreadPool.h\"\n\n\nClientSocket::ClientSocket(int tag) : tag_(tag)\n{\n}\n\nClientSocket::~ClientSocket()\n{\n    if (localSock_ != INVALID_SOCKET)\n        WRN << \"Destruct ClientSocket \" << localSock_;\n}\n\nbool ClientSocket::Connect(const SocketAddr& dst)\n{\n    if (dst.Empty())\n        return false;\n\n    if (INVALID_SOCKET != localSock_)\n        return false;\n\n    peerAddr_ = dst;\n\n    localSock_ = CreateTCPSocket();\n    SetNonBlock(localSock_);\n    SetNodelay(localSock_);\n    SetRcvBuf(localSock_);\n    SetSndBuf(localSock_);\n\n    int  result = ::connect(localSock_, (sockaddr*)&peerAddr_.GetAddr(), sizeof(sockaddr_in));\n\n    if (0 == result)\n    {\n        INF << \"ClientSocket immediately connected to [\"\n            << peerAddr_.GetIP()\n            << \":\"\n            << peerAddr_.GetPort()\n            << \"]\";\n       \n        Server::Instance()->NewConnection(localSock_, tag_, onConnectFail_);\n        localSock_ = INVALID_SOCKET;\n        return  true;\n    }\n    else\n    {\n        if (EINPROGRESS == errno)\n        {\n            \n            INF << \"EINPROGRESS: ClientSocket connected to (\"\n                << peerAddr_.GetIP()\n                << \":\"\n                << peerAddr_.GetPort()\n                << \")\";\n            \n            Internal::NetThreadPool::Instance().AddSocket(shared_from_this(), EventTypeWrite);\n            epollOut_ = true;\n            return true;\n        }\n\n        ERR << \"Failed: ClientSocket connected to (\"\n            << peerAddr_.GetIP()\n            << \":\"\n            << peerAddr_.GetPort()\n            << \")\";\n\n        if (onConnectFail_)\n            onConnectFail_();\n\n        return false;\n    }\n    \n    return true;\n}\n\n\nbool ClientSocket::OnWritable()\n{\n    epollOut_ = false;\n    int         error  = 0;\n    socklen_t   len    = sizeof error;\n\n    bool bSucc = (::getsockopt(localSock_, SOL_SOCKET, SO_ERROR, (char*)&error, &len) >= 0 && 0 == error);\n    if (!bSucc)    \n    {\n        errno = error;\n        ERR << \"Failed: ClientSocket connected to (\"\n            << peerAddr_.GetIP()\n            << \":\"\n            << peerAddr_.GetPort()\n            << \"), error:\" << error;\n\n        return false;\n    }\n\n    INF << \"Success: ClientSocket connected to (\"\n        << peerAddr_.GetIP()\n        << \":\"\n        << peerAddr_.GetPort() << \")\";\n\n    Server::Instance()->NewConnection(localSock_, tag_, onConnectFail_);\n    localSock_ = INVALID_SOCKET; \n\n    return true;\n}\n\nbool ClientSocket::OnError()\n{\n    if (Socket::OnError())\n    {\n        ERR << __FUNCTION__ << \" connected to (\"\n            << peerAddr_.GetIP()\n            << \":\"\n            << peerAddr_.GetPort() << \")\";\n        \n        if (onConnectFail_)\n            onConnectFail_();\n\n        return  true;\n    }\n        \n    return  false;\n}\n\n"
  },
  {
    "path": "QBase/ClientSocket.h",
    "content": "\n#ifndef BERT_CLIENTSOCKET_H\n#define BERT_CLIENTSOCKET_H\n\n#include <functional>\n#include \"Socket.h\"\n\n// Abstraction for a TCP client socket\nclass ClientSocket : public Socket\n{\npublic:\n    explicit\n    ClientSocket(int tag);\n    ~ClientSocket();\n    bool    Connect(const SocketAddr& addr);\n    bool    OnWritable();\n    bool    OnError();\n    SocketType GetSockType() const { return SocketType_Client; }\n\n    void SetFailCallback(const std::function<void ()>& cb) { onConnectFail_ = cb; }\n\nprivate:\n    const int tag_;\n    SocketAddr peerAddr_;\n    std::function<void ()> onConnectFail_;\n};\n\n#endif\n\n"
  },
  {
    "path": "QBase/ConfigParser.cc",
    "content": "#include <vector>\n#include \"ConfigParser.h\"\n#include \"Log/MemoryFile.h\"\n\nstatic const int  SPACE     = ' ';\nstatic const int  TAB       = '\\t';\nstatic const int  NEWLINE   = '\\n';\nstatic const int  COMMENT   = '#';\n\n\nstatic size_t  SkipBlank(const char* data, size_t len, size_t off)\n{\n    while (++ off < len)\n    {\n        if (SPACE != data[off] && TAB != data[off])\n        {\n            -- off;\n            break;\n        }\n    }\n\n    return off;\n}\n\nbool ConfigParser::Load(const char* FileName)\n{\n    InputMemoryFile  file;\n    if (!file.Open(FileName))\n        return false; // no such file\n\n    data_.clear();\n\n    size_t      maxLen = size_t(-1);\n    const char* data = file.Read(maxLen);\n\n    bool  bReadKey = true;\n    std::string  key, value;\n    key.reserve(64);\n    value.reserve(64);\n\n    size_t  off = 0;\n    while (off < maxLen)\n    {\n        switch (data[off])\n        {\n            case COMMENT:\n                while (++ off < maxLen)\n                {\n                    if (NEWLINE == data[off])\n                    {\n                        -- off;\n                        break;\n                    }\n                }\n                break;\n\n            case NEWLINE:\n                bReadKey = true;\n\n                if (!key.empty())\n                {\n                    data_[key].push_back(value);\n                    \n                    key.clear();\n                    value.clear();\n                }\n                \n                off = SkipBlank(data, maxLen, off);\n                break;\n            \n            case SPACE:\n            case TAB:\n                // 支持value中有空格\n                if (bReadKey)\n                {\n                    bReadKey = false;\n                    off = SkipBlank(data, maxLen, off); // 跳过所有分界空格\n                }\n                else\n                {\n                    value += data[off];\n                }\n                break;\n            \n            case '\\r':\n                break;\n            \n            default:\n                if (bReadKey)\n                    key += data[off];\n                else\n                    value += data[off];\n\n                break;\n        }\n\n        ++ off;\n    }\n    \n    file.Close();\n    return true;\n}\n\nconst std::vector<std::string>& ConfigParser::GetDataVector(const char* key) const\n{\n    auto it = data_.find(key);\n    if (it == data_.end())\n    {\n        static const std::vector<std::string> kEmpty;\n        return kEmpty;\n    }\n    \n    return it->second;\n}\n\n#ifdef CONFIG_DEBUG\nint main()\n{\n    ConfigParser   csv;\n    csv.Load(\"config\");\n    csv.Print();\n\t\n    std::cout << \"=====================\" << std::endl;\n}\n#endif\n\n"
  },
  {
    "path": "QBase/ConfigParser.h",
    "content": "#ifndef BERT_CONFIGPARSER_H\n#define BERT_CONFIGPARSER_H\n\n#include <map>\n#include <string>\n#include <sstream>\n\n#ifdef CONFIG_DEBUG\n#include <iostream>\n#endif\n\nclass ConfigParser\n{\npublic:\n    bool Load(const char* FileName);\n\n    template <typename T>\n    T   GetData(const char* key, const T& default_ = T()) const;\n\n    const std::vector<std::string>& GetDataVector(const char* key) const;\n    \n\n#ifdef CONFIG_DEBUG\n    void Print()\n    {\n        std::cout << \"//////////////////\"<< std::endl;\n        std::map<std::string, std::string>::const_iterator it = data_.begin();\n        while (it != data_.end())\n        {\n            std::cout << it->first << \":\" << it->second << \"\\n\";\n            ++ it;\n        }\n    }\n#endif\n\nprivate:\n    typedef std::map<std::string, std::vector<std::string> > Data;\n    \n    Data data_;\n\n    template <typename T>\n    T  _ToType(const std::string& data) const;\n};\n\n\ntemplate <typename T>\ninline  T  ConfigParser::_ToType(const std::string& data) const\n{\n    T        t;\n    std::istringstream  os(data);\n    os >> t;\n    return  t;\n}\n\ntemplate <>\ninline  const char*  ConfigParser::_ToType<const char* >(const std::string& data) const\n{\n    return  data.c_str();\n}\n\ntemplate <>\ninline  std::string  ConfigParser::_ToType<std::string >(const std::string& data) const\n{\n    return  data;\n}\n\n\ntemplate <typename T>\ninline  T  ConfigParser::GetData(const char* key, const T& default_) const\n{\n    auto it = data_.find(key);\n    if (it == data_.end())\n        return default_;\n\n    return  _ToType<T>(it->second[0]); // only return first value\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/Delegate.h",
    "content": "#include <functional>\n#include <list>\n\n\ntemplate <typename T>\nclass Delegate;\n\ntemplate <typename... Args>\nclass Delegate<void (Args...)>\n{\npublic:\n    typedef Delegate<void (Args...)> Self;\n\n    Delegate() = default;\n\n    Delegate(const Self& ) = delete;\n    Self& operator= (const Self& ) = delete;\n\n    template <typename F>\n    Delegate(F&& f)\n    {\n        connect(std::forward<F>(f));\n    }\n\n    Delegate(Self&& other) : funcs_(std::move(other.funcs_))\n    {\n    }\n    \n    template <typename F>\n    Self& operator+=(F&& f)\n    {\n        connect(std::forward<F>(f));\n        return *this;\n    }\n    \n    template <typename F>\n    Self& operator-=(F&& f)\n    {\n        disconnect(std::forward<F>(f));\n        return *this;\n    }\n    \n    void operator()(Args&&... args)\n    {\n        call(std::forward<Args>(args)...);\n    }\n    \nprivate:\n    std::list<std::function<void (Args ...)> > funcs_;\n    \n    template <typename F>\n    void connect(F&& f)\n    {\n        funcs_.emplace_back(std::forward<F>(f));\n    }\n\n    template <typename F>\n    void disconnect(F&& f)\n    {\n        //std::cerr << \"&f = \" << typeid(&f).name() << \", and target = \" << typeid(decltype(std::addressof(f))).name() << std::endl;\n        for (auto it(funcs_.begin()); it != funcs_.end(); ++ it)\n        {\n            const auto& target = it->template target<decltype(std::addressof(f))>();\n            if (target)\n            {\n                if(*target == &f)\n                {\n                    funcs_.erase(it);\n                    return;\n                }\n            }\n            else\n            {\n                const auto& target2 = it->template target<typename std::remove_reference<decltype(f)>::type>();\n\n                // the function object must implement operator ==\n                if (target2 && *target2 == f)\n                {\n                    funcs_.erase(it);\n                    return;\n                }\n            }\n        }\n    }\n\n    void call(Args&&... args)\n    {\n        // But what if rvalue args? FIXME\n        for (const auto& f : funcs_)\n            f(std::forward<Args>(args)...);\n    }\n};\n\n"
  },
  {
    "path": "QBase/EPoller.cc",
    "content": "\n#if defined(__gnu_linux__)\n\n#include \"EPoller.h\"\n#include \"Log/Logger.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\nnamespace Epoll\n{\n    bool ModSocket(int epfd, int socket, uint32_t events, void* ptr);\n\n    bool AddSocket(int epfd, int socket, uint32_t events, void* ptr)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event  ev;\n        ev.data.ptr= ptr;\n\n        ev.events  = 0;\n        if (events & EventTypeRead)\n            ev.events |= EPOLLIN;\n        if (events & EventTypeWrite)\n            ev.events |= EPOLLOUT;\n\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_ADD, socket, &ev);\n    }\n\n    bool DelSocket(int epfd, int socket)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event dummy;\n\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_DEL, socket, &dummy) ;\n    }\n\n    bool ModSocket(int epfd, int socket, uint32_t events, void* ptr)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event  ev;\n        ev.data.ptr= ptr;\n\n        ev.events  = 0;\n        if (events & EventTypeRead)\n            ev.events |= EPOLLIN;\n        if (events & EventTypeWrite)\n            ev.events |= EPOLLOUT;\n\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_MOD, socket, &ev);\n    }\n}\n\n\nEpoller::Epoller()\n{\n    multiplexer_ = ::epoll_create(512);\n    INF << \"create epoll:  \" << multiplexer_;\n}\n\nEpoller::~Epoller()\n{\n    INF << \"close epoll:  \" << multiplexer_;\n    if (multiplexer_ != -1)  \n        ::close(multiplexer_);\n}\n\nbool Epoller::AddSocket(int sock, int events, void* userPtr)\n{\n    if (Epoll::AddSocket(multiplexer_, sock, events, userPtr))\n        return  true;\n\n    return (errno == EEXIST) && ModSocket(sock, events, userPtr);\n}\n    \nbool Epoller::DelSocket(int sock, int events)\n{\n    return Epoll::DelSocket(multiplexer_, sock);\n}\n\n   \nbool Epoller::ModSocket(int sock, int events, void* userPtr)\n{\n    if (events == 0)\n        return DelSocket(sock, 0);\n\n    if (Epoll::ModSocket(multiplexer_, sock, events, userPtr))\n        return  true;\n\n    return  errno == ENOENT && AddSocket(sock, events, userPtr);\n}\n\n\nint Epoller::Poll(std::vector<FiredEvent>& events, size_t  maxEvent, int timeoutMs)\n{\n    if (maxEvent == 0)\n        return 0;\n\n    while (events_.size() < maxEvent)\n        events_.resize(2 * events_.size() + 1);\n\n    int nFired = TEMP_FAILURE_RETRY(::epoll_wait(multiplexer_, &events_[0], maxEvent, timeoutMs));\n    if (nFired == -1 && errno != EINTR && errno != EWOULDBLOCK)\n        return -1;\n\n    if (nFired > 0 && static_cast<size_t>(nFired) > events.size())\n        events.resize(nFired);\n\n    for (int i = 0; i < nFired; ++ i)\n    {\n        FiredEvent& fired = events[i];\n        fired.events   = 0;\n        fired.userdata = events_[i].data.ptr;\n\n        if (events_[i].events & EPOLLIN)\n            fired.events  |= EventTypeRead;\n\n        if (events_[i].events & EPOLLOUT)\n            fired.events  |= EventTypeWrite;\n\n        if (events_[i].events & (EPOLLERR | EPOLLHUP))\n            fired.events  |= EventTypeError;\n    }\n\n    return nFired;\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/EPoller.h",
    "content": "#ifndef BERT_EPOLLER_H\n#define BERT_EPOLLER_H\n\n#if defined(__gnu_linux__)\n\n#include <sys/epoll.h>\n#include <vector>\n#include \"Poller.h\"\n\nclass Epoller : public Poller\n{\npublic:\n    Epoller();\n   ~Epoller();\n\n    bool AddSocket(int sock, int events, void* userPtr);\n    bool ModSocket(int sock, int events, void* userPtr);\n    bool DelSocket(int sock, int events);\n\n    int Poll(std::vector<FiredEvent>& events, std::size_t maxEvent, int timeoutMs);\n\nprivate:\n    std::vector<epoll_event> events_;    \n};\n\n#endif\n\n#endif\n\n"
  },
  {
    "path": "QBase/Kqueue.cc",
    "content": "#if defined(__APPLE__)\n\n#include \"Kqueue.h\"\n#include \"Log/Logger.h\"\n\n#include <sys/event.h>\n#include <errno.h>\n#include <unistd.h>\n\n\nKqueue::Kqueue()\n{\n    multiplexer_ = ::kqueue();\n    INF << \"create kqueue:  \" << multiplexer_;\n}\n\nKqueue::~Kqueue()\n{\n    INF << \"close kqueue: \" << multiplexer_;\n    if (multiplexer_ != -1)  \n        ::close(multiplexer_);\n}\n\nbool Kqueue::AddSocket(int sock, int events, void* userPtr)\n{\n     struct kevent change[2];\n         \n     int  cnt = 0;\n     \n     if (events & EventTypeRead)\n     {\n         EV_SET(change + cnt, sock, EVFILT_READ, EV_ADD, 0, 0, userPtr);\n         ++ cnt;\n     }\n                 \n     if (events & EventTypeWrite)\n     {\n         EV_SET(change + cnt, sock, EVFILT_WRITE, EV_ADD, 0, 0, userPtr);\n         ++ cnt;\n     }\n                     \n     return kevent(multiplexer_, change, cnt, NULL, 0, NULL) != -1;\n}\n    \nbool Kqueue::DelSocket(int sock, int events)\n{\n    struct kevent change[2];\n    int cnt = 0;\n\n    if (events & EventTypeRead)\n    {\n        EV_SET(change + cnt, sock, EVFILT_READ, EV_DELETE, 0, 0, NULL);\n        ++ cnt;\n    }\n\n    if (events & EventTypeWrite)\n    {\n        EV_SET(change + cnt, sock, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);\n        ++ cnt;\n    }\n                    \n    if (cnt == 0)\n        return false;\n                        \n    return -1 != kevent(multiplexer_, change, cnt, NULL, 0, NULL);\n}\n\n   \nbool Kqueue::ModSocket(int sock, int events, void* userPtr)\n{\n    bool ret = DelSocket(sock, EventTypeRead | EventTypeWrite);\n    if (events == 0)\n        return ret;\n\n    return AddSocket(sock, events, userPtr);\n}\n\n\nint Kqueue::Poll(std::vector<FiredEvent>& events, std::size_t maxEvent, int timeoutMs)\n{\n    if (maxEvent == 0)\n        return 0;\n\n    while (events_.size() < maxEvent)\n        events_.resize(2 * events_.size() + 1);\n\n    struct timespec* pTimeout = NULL;  \n    struct timespec  timeout;\n    if (timeoutMs >= 0)\n    {\n        pTimeout = &timeout;\n        timeout.tv_sec  = timeoutMs / 1000;\n        timeout.tv_nsec = timeoutMs % 1000 * 1000000;\n    }\n\n    int nFired = ::kevent(multiplexer_, NULL, 0, &events_[0], static_cast<int>(maxEvent), pTimeout);\n    if (nFired == -1)\n        return -1;\n\n    if (nFired > 0 && static_cast<size_t>(nFired) > events.size())\n        events.resize(nFired);\n\n    for (int i = 0; i < nFired; ++ i)\n    {\n        FiredEvent& fired = events[i];\n        fired.events   = 0;\n        fired.userdata = events_[i].udata;\n\n        if (events_[i].filter == EVFILT_READ)\n            fired.events  |= EventTypeRead;\n\n        if (events_[i].filter == EVFILT_WRITE)\n            fired.events  |= EventTypeWrite;\n\n        if (events_[i].flags & EV_ERROR)\n            fired.events  |= EventTypeError;\n    }\n\n    return nFired;\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/Kqueue.h",
    "content": "#ifndef BERT_KQUEUE_H\n#define BERT_KQUEUE_H\n\n#if defined(__APPLE__)\n\n#include \"Poller.h\"\n#include <vector>\n\nclass Kqueue : public Poller\n{\npublic:\n    Kqueue();\n   ~Kqueue();\n\n    bool AddSocket(int sock, int events, void* userPtr);\n    bool ModSocket(int sock, int events, void* userPtr);\n    bool DelSocket(int sock, int events);\n\n    int Poll(std::vector<FiredEvent>& events, std::size_t maxEvent, int timeoutMs);\n\nprivate:\n    std::vector<struct kevent> events_;    \n};\n\n#endif\n\n#endif\n\n"
  },
  {
    "path": "QBase/ListenSocket.cc",
    "content": "\n#include <errno.h>\n#include <sys/socket.h>\n#include <cstdlib>\n#include <cstring>\n#include <cassert>\n#include \"Server.h\"\n#include \"NetThreadPool.h\"\n#include \"ListenSocket.h\"\n#include \"Log/Logger.h\"\n\nnamespace Internal\n{\n\nconst int ListenSocket::LISTENQ = 1024;\n\nListenSocket::ListenSocket(int tag) :\n    localPort_(INVALID_PORT),\n    tag_(tag)\n{\n}\n\nListenSocket::~ListenSocket()\n{\n    Server::Instance()->DelListenSock(localSock_);\n    USR << \"Close ListenSocket \" << localSock_;\n}\n\nbool ListenSocket::Bind(const SocketAddr& addr)\n{\n    if (addr.Empty())\n        return  false;\n\n    if (localSock_ != INVALID_SOCKET)\n        return false;\n\n    localPort_ = addr.GetPort();\n\n    localSock_ = CreateTCPSocket();\n    SetNonBlock(localSock_);\n    SetNodelay(localSock_);\n    SetReuseAddr(localSock_);\n    SetRcvBuf(localSock_);\n    SetSndBuf(localSock_);\n\n    struct sockaddr_in serv = addr.GetAddr();\n\n    int ret = ::bind(localSock_, (struct sockaddr*)&serv, sizeof serv);\n    if (SOCKET_ERROR == ret)\n    {\n        CloseSocket(localSock_);\n        ERR << \"Cannot bind port \" << addr.GetPort();\n        return false;\n    }\n    ret = ::listen(localSock_, ListenSocket::LISTENQ);\n    if (SOCKET_ERROR == ret)\n    {\n        CloseSocket(localSock_);\n        ERR << \"Cannot listen on port \" << addr.GetPort();\n        return false;\n    }\n\n    if (!NetThreadPool::Instance().AddSocket(shared_from_this(), EventTypeRead))\n        return false;\n\n    USR << \"Success: listen on (\"\n        << addr.GetIP()\n        << \":\"\n        << addr.GetPort()\n        << \")\";\n    \n    return true;\n}\n\nint ListenSocket::_Accept()\n{\n    socklen_t   addrLength = sizeof addrClient_;\n    return ::accept(localSock_, (struct sockaddr *)&addrClient_, &addrLength);\n}\n\nbool ListenSocket::OnReadable()\n{\n    while (true)\n    {\n        int connfd = _Accept();\n        if (connfd >= 0)\n        {\n            Server::Instance()->NewConnection(connfd, tag_);\n        }\n        else\n        {\n            bool result = false;\n            switch (errno)\n            {\n            case EWOULDBLOCK:\n            case ECONNABORTED:\n            case EINTR:\n                result = true;\n                break;\n\n            case EMFILE:\n            case ENFILE:\n                ERR << \"Not enough file descriptor available!!!\";\n                result = true;\n                break;\n\n            case ENOBUFS:\n                ERR << \"Not enough memory\";          \n                result = true;\n\n            default:\n                ERR << \"When accept, unknown error happened : \" << errno;          \n                break;\n            }\n\n            return result;\n        }\n    }\n    \n    return true;\n}\n\nbool ListenSocket::OnWritable()\n{\n    return false;\n}\n\nbool ListenSocket::OnError()\n{\n    if (Socket::OnError())\n    {\n        Server::Instance()->DelListenSock(localSock_);\n        return  true;\n    }\n\n    return false;\n}\n\n}\n\n"
  },
  {
    "path": "QBase/ListenSocket.h",
    "content": "\n#ifndef BERT_LISTENSOCKET_H\n#define BERT_LISTENSOCKET_H\n\n#include \"Socket.h\"\n\nnamespace Internal\n{\n\nclass ListenSocket : public Socket\n{\n    static const int LISTENQ;\npublic:\n    explicit\n    ListenSocket(int tag);\n    ~ListenSocket();\n    \n    SocketType GetSocketType() const { return SocketType_Listen; }\n\n    bool Bind(const SocketAddr& addr);\n    bool OnReadable();\n    bool OnWritable();\n    bool OnError();\n\nprivate:\n    int _Accept();\n    sockaddr_in     addrClient_;\n    unsigned short  localPort_;\n    const int       tag_;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "QBase/Log/Logger.cc",
    "content": "\n#include <iostream>\n#include <cstdlib>\n#include <cstdio>\n#include <cstring>\n#include <cstdarg>\n#include <errno.h>\n#include <sys/stat.h>\n\n\nenum LogColor\n{\n    RED_COLOR    = 1,\n    GREEN_COLOR     ,\n    YELLOW_COLOR    ,\n    NORMAL_COLOR    ,\n    BLUE_COLOR      ,\n    PURPLE_COLOR    ,\n    WHITE_COLOR     ,\n    COLOR_MAX       ,\n} ;\n\n#include \"Logger.h\"\n#include \"../Timer.h\"\n\n\nstatic const size_t DEFAULT_LOGFILESIZE = 32 * 1024 * 1024;\nstatic const size_t PREFIX_LEVEL_LEN    = 6;\nstatic const size_t PREFIX_TIME_LEN     = 24;\n\nunsigned int ConvertLogLevel(const std::string& level)\n{\n    unsigned int l = logALL;\n    \n    if (level == \"debug\")\n    {\n        ;\n    }\n    else if (level == \"verbose\")\n    {\n        l &= ~logDEBUG;\n    }\n    else if (level == \"notice\")\n    {\n        l &= ~logDEBUG;\n        l &= ~logINFO;\n    }\n    else if (level == \"warning\")\n    {\n        l &= ~logDEBUG;\n        l &= ~logINFO;\n        l &= ~logWARN; // redis warning is my error\n    }\n    else if (level == \"none\")\n    {\n        l = 0;\n    }\n    \n    return l;\n}\n\nstatic bool MakeDir(const char* pDir)\n{\n    if (mkdir(pDir, 0755) != 0)\n    {\n        if (EEXIST != errno)\n            return false;\n    }\n    \n    return true;\n}\n\n__thread Logger*  g_log = nullptr;\n__thread unsigned g_logLevel;\n__thread unsigned g_logDest;\n\nLogger::Logger() : level_(0),\n                   dest_(0)\n{\n    lastLogMSecond_ = lastLogSecond_ = -1;\n    _Reset();\n}\n\nLogger::~Logger()\n{\n    _CloseLogFile();\n}\n\nbool Logger::Init(unsigned int level, unsigned int dest, const char* pDir)\n{\n    level_      = level;\n    dest_       = dest;\n    directory_  = pDir ? pDir : \".\";\n\n    if (0 == level_)\n    {\n        return  true;\n    }\n  \n    if (dest_ & logFILE)\n    {\n        if (directory_ == \".\" ||\n            MakeDir(directory_.c_str()))\n        {\n            _OpenLogFile(_MakeFileName().c_str());\n            return true;\n        }\n        \n        return false;\n    }\n\n    if (!(dest_ & logConsole))\n    {\n        std::cerr << \"log has no output, but loglevel is \" << level << std::endl;\n        return false;\n    }\n            \n    return true;\n}\n\nbool Logger::_CheckChangeFile()\n{\n    if (!file_.IsOpen())\n        return true;\n    \n    return file_.Offset() + MAXLINE_LOG > DEFAULT_LOGFILESIZE;\n}\n\nconst std::string& Logger::_MakeFileName()\n{\n    char   name[32];\n    Time   now;\n    size_t len = now.FormatTime(name);\n    name[len] = '\\0';\n\n    fileName_  = directory_ + \"/\" + name;\n    fileName_ += \".log\";\n\n    return fileName_;\n}\n\nbool Logger::_OpenLogFile(const char* name)\n{ \n    return  file_.Open(name, true);\n}\n\nvoid Logger::_CloseLogFile()\n{\n    return file_.Close();\n}\n\n\nvoid Logger::Flush(enum LogLevel level)\n{\n    if (IsLevelForbid(curLevel_) ||\n        !(level & curLevel_) ||\n         (pos_ <= PREFIX_TIME_LEN + PREFIX_LEVEL_LEN)) \n    {\n        _Reset();\n        return;\n    }\n    \n    time_.Now();\n#if 0\n    time_.FormatTime(tmpBuffer_);\n#else\n    auto seconds = time_.MilliSeconds() / 1000;\n    if (seconds != lastLogSecond_)\n    {\n        time_.FormatTime(tmpBuffer_);\n        lastLogSecond_ = seconds;\n    }\n    else\n    {\n        auto msec = time_.MilliSeconds() % 1000;\n        if (msec != lastLogMSecond_)\n        {\n            snprintf(tmpBuffer_ + 20, 4, \"%03d\", static_cast<int>(msec));\n            tmpBuffer_[23] = ']';\n            lastLogMSecond_ = msec;\n        }\n    }\n#endif\n\n    switch(level)\n    {\n    case logINFO:\n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[INF]:\", PREFIX_LEVEL_LEN);\n        break;\n\n    case logDEBUG:\n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[DBG]:\", PREFIX_LEVEL_LEN);\n        break;\n\n    case logWARN:\n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[WRN]:\", PREFIX_LEVEL_LEN);\n        break;\n\n    case logERROR:\n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[ERR]:\", PREFIX_LEVEL_LEN);\n        break;\n\n    case logUSR:\n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[USR]:\", PREFIX_LEVEL_LEN);\n        break;\n\n    default:    \n        memcpy(tmpBuffer_ + PREFIX_TIME_LEN, \"[???]:\", PREFIX_LEVEL_LEN);\n        break;\n    }\n\n    tmpBuffer_[pos_ ++] = '\\n';\n    tmpBuffer_[pos_] = '\\0';\n    \n    if (LogManager::Instance().IsShutdown())\n    {\n        std::cout << tmpBuffer_;\n    }\n    else\n    {\n        // Format: level info, length info, log msg\n        int logLevel = level;\n\n        BufferSequence  contents;\n        contents.count = 3;\n\n        contents.buffers[0].iov_base = &logLevel;\n        contents.buffers[0].iov_len  = sizeof logLevel;\n        contents.buffers[1].iov_base = &pos_;\n        contents.buffers[1].iov_len  = sizeof pos_;\n        contents.buffers[2].iov_base = tmpBuffer_;\n        contents.buffers[2].iov_len  = pos_;\n\n        buffer_.Write(contents);\n    }\n\n    _Reset();\n}\n\nvoid Logger::_Color(unsigned int color)\n{\n#if defined(__gnu_linux__)\n    const char* colorstrings[COLOR_MAX] = {\n        \"\",\n        \"\\033[1;31;40m\",\n        \"\\033[1;32;40m\",\n        \"\\033[1;33;40m\",\n        \"\\033[0m\",\n        \"\\033[1;34;40m\",\n        \"\\033[1;35;40m\",\n        \"\\033[1;37;40m\",\n    };\n\n    fprintf(stdout, \"%s\", colorstrings[color]);\n#endif\n}\n\nLogger&  Logger::operator<< (const char* msg)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n\n    const auto len = strlen(msg);\n    if (pos_ + len >= MAXLINE_LOG)  \n    {\n        return *this;\n    }\n\n    memcpy(tmpBuffer_ + pos_, msg, len);\n    pos_ += len;\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (const unsigned char* msg)\n{\n    return operator<<(reinterpret_cast<const char*>(msg));\n}\n\nLogger&  Logger::operator<< (const std::string& msg)\n{\n    return operator<<(msg.c_str());\n}\n\nLogger&  Logger::operator<< (void* ptr)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    \n    if (pos_ + 18 < MAXLINE_LOG)\n    {  \n        unsigned long ptrValue = (unsigned long)ptr;\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%#018lx\", ptrValue);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\n\nLogger&  Logger::operator<< (unsigned char a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n\n    if (pos_ + 3 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%hhd\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\n\nLogger&  Logger::operator<< (char a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 3 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%hhu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned short a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 5 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%hu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (short a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 5 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%hd\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned int a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 10 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%u\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (int a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 10 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%d\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned long a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 20 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%lu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (long a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 20 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%ld\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned long long a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 20 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%llu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (long long a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 20 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%lld\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (double a)\n{\n    if (IsLevelForbid(curLevel_))\n    {\n        return *this;\n    }\n    if (pos_ + 20 < MAXLINE_LOG)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, \"%.6g\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n    \n    return  *this;\n}\n\nbool Logger::Update()\n{\n    BufferSequence  data;\n    buffer_.ProcessBuffer(data);\n    \n    AttachedBuffer  abf(data);\n    auto nWritten = _Log(abf.ReadAddr(), abf.ReadableSize());\n    buffer_.Skip(nWritten);\n    \n    return nWritten != 0;\n}\n\nvoid   Logger::_Reset()\n{\n    curLevel_ = 0;\n    pos_  = PREFIX_LEVEL_LEN + PREFIX_TIME_LEN ;\n}\n\nsize_t  Logger::_Log(const char* data, size_t dataLen)\n{\n    const auto minLogSize = sizeof(int) + sizeof(size_t);\n\n    size_t   nOffset = 0;\n    while (nOffset + minLogSize < dataLen)\n    {\n        int  level = *(int*)(data + nOffset);\n        size_t len = *(size_t* )(data + nOffset + sizeof(int));\n        if (dataLen < nOffset + minLogSize + len)\n        {\n            std::cerr << \"_WriteLog skip 0!!!\\n \";\n            break;\n        }\n\n        _WriteLog(level, len, data + nOffset + minLogSize);\n        nOffset += minLogSize + len;\n    }\n\n    return  nOffset;\n}\n\n\n\nvoid Logger::_WriteLog(int level, size_t nLen, const char* data)\n{\n    assert (nLen > 0 && data);\n    \n    if (dest_ & logConsole)\n    {\n        switch (level)\n        {\n        case logINFO:\n            _Color(GREEN_COLOR);\n            break;\n\n        case logDEBUG:\n            _Color(WHITE_COLOR);\n            break;\n\n        case logWARN:\n            _Color(YELLOW_COLOR);\n            break;\n\n        case logERROR:\n            _Color(RED_COLOR);\n            break;\n\n        case logUSR:\n            _Color(PURPLE_COLOR);\n            break;\n\n        default:\n            _Color(RED_COLOR);\n            break;\n        }\n\n        fprintf(stdout, \"%.*s\", static_cast<int>(nLen), data);\n        _Color(NORMAL_COLOR);\n    }\n\n    if (dest_ & logFILE)\n    {\n        while (_CheckChangeFile())\n        {\n            _CloseLogFile();\n            if (!_OpenLogFile(_MakeFileName().c_str()))\n            {   //OOPS!!! IMPOSSIBLE!\n                break;\n            }\n        }\n\n        assert (file_.IsOpen());\n        file_.Write(data, nLen);\n    }\n}\n\n\n\nLogHelper::LogHelper(LogLevel level) : level_(level)    \n{\n}\n    \nLogger& LogHelper::operator=(Logger& log) \n{\n    log.Flush(level_);\n    return  log;\n}\n\n\n\nLogManager& LogManager::Instance()\n{\n    static LogManager mgr;\n    return mgr;\n}\n\nLogManager::LogManager()\n{\n    nullLog_.Init(0);\n    logThread_.reset(new LogThread);\n}\n\nLogManager::~LogManager()\n{\n    for (auto log : logs_)\n    {\n        delete log;\n    }\n}\n\nLogger*  LogManager::CreateLog(unsigned int level ,\n               unsigned int dest ,\n               const char* pDir)\n{\n    Logger*  pLog(new Logger);\n    if (!pLog->Init(level, dest, pDir))\n    {\n        delete pLog;\n        return &nullLog_;\n    }\n    else\n    {\n        std::lock_guard<std::mutex>  guard(logsMutex_);\n        logs_.insert(pLog);\n    }\n\n    return pLog;\n}\n\nbool LogManager::StartLog()\n{\n    std::cout << \"start log thread\\n\";\n\n    assert (!logThread_->IsAlive());\n    logThread_->SetAlive();\n    shutdown_ = false;\n\n    ThreadPool::Instance().ExecuteTask(std::bind(&LogThread::Run, logThread_));\n    return true;\n}\n\nvoid LogManager::StopLog()\n{\n    std::cout << \"stop log thread\\n\";\n    shutdown_ = true;\n    logThread_->Stop();\n}\n\nbool LogManager::Update()\n{\n    bool busy = false;\n    \n    std::lock_guard<std::mutex>  guard(logsMutex_);\n  \n    for (auto log : logs_)\n    {\n        if (log->Update() && !busy)\n        {\n            busy = true;\n        }\n    }\n\n    return  busy;\n}\n\n\nvoid  LogManager::LogThread::Run()\n{\n    while (IsAlive())\n    {\n        if (!LogManager::Instance().Update())\n            std::this_thread::sleep_for(std::chrono::milliseconds(1));\n    }\n}\n"
  },
  {
    "path": "QBase/Log/Logger.h",
    "content": "#ifndef BERT_LOGGER_H\n#define BERT_LOGGER_H\n\n#include <string>\n#include <set>\n#include <memory>\n\n#include \"../Threads/ThreadPool.h\"\n#include \"../AsyncBuffer.h\"\n#include \"MemoryFile.h\"\n#include \"../Timer.h\"\n\nenum LogLevel\n{\n    logINFO     = 0x01 << 0,\n    logDEBUG    = 0x01 << 1,\n    logWARN     = 0x01 << 2,\n    logERROR    = 0x01 << 3,\n    logUSR      = 0x01 << 4,\n    logALL      = 0xFFFFFFFF,\n};\n\nenum LogDest\n{\n    logConsole  = 0x01 << 0,\n    logFILE     = 0x01 << 1,\n    logSocket   = 0x01 << 2,  // TODO : LOG SERVER\n};\n\nunsigned int ConvertLogLevel(const std::string& level);\n\nclass Logger\n{\npublic:\n\n\tfriend class LogManager;\n\n    Logger(const Logger& ) = delete;\n    void operator= (const Logger& ) = delete;\n\n    bool Init(unsigned int level = logDEBUG,\n              unsigned int dest = logConsole,\n              const char* pDir  = 0);\n    \n    void Flush(LogLevel  level);\n    bool IsLevelForbid(unsigned int level)  {  return  !(level & level_); };\n\n    Logger&  operator<<(const char* msg);\n    Logger&  operator<<(const unsigned char* msg);\n    Logger&  operator<<(const std::string& msg);\n    Logger&  operator<<(void* );\n    Logger&  operator<<(unsigned char a);\n    Logger&  operator<<(char a);\n    Logger&  operator<<(unsigned short a);\n    Logger&  operator<<(short a);\n    Logger&  operator<<(unsigned int a);\n    Logger&  operator<<(int a);\n    Logger&  operator<<(unsigned long a);\n    Logger&  operator<<(long a);\n    Logger&  operator<<(unsigned long long a);\n    Logger&  operator<<(long long a);\n    Logger&  operator<<(double a);\n\n    Logger& SetCurLevel(unsigned int level)\n    {\n        curLevel_ = level;\n        return *this;\n    }\n\n    bool   Update();\n\nprivate:\n    Logger();\n   ~Logger();\n\n    static const size_t MAXLINE_LOG = 2048; // TODO\n    char            tmpBuffer_[MAXLINE_LOG];\n    std::size_t     pos_;\n    \n    Time            time_;\n\n    unsigned int    level_;\n    std::string     directory_;\n    unsigned int    dest_;\n    std::string     fileName_;\n\n    unsigned int    curLevel_;\n\n    AsyncBuffer    buffer_;\n    OutputMemoryFile file_;\n    \n    // for optimization\n    uint64_t        lastLogSecond_;\n    uint64_t        lastLogMSecond_;\n    \n    std::size_t     _Log(const char* data, std::size_t len);\n\n    bool    _CheckChangeFile();\n    const std::string& _MakeFileName();\n    bool    _OpenLogFile(const char* name);\n    void    _CloseLogFile();\n    void    _WriteLog(int level, std::size_t nLen, const char* data);\n    void    _Color(unsigned int color);\n    void    _Reset();\n};\n\n\n\nclass LogHelper\n{\npublic:\n    LogHelper(LogLevel level);\n    Logger& operator=(Logger& log);\n\nprivate:\n    LogLevel    level_;\n};\n\n\n\nclass LogManager\n{\npublic:\n    static LogManager& Instance();\n\n    ~LogManager();\n    \n    LogManager(const LogManager& ) = delete;\n    void operator=(const LogManager& ) = delete;\n\n    Logger*  CreateLog(unsigned int level = logDEBUG,\n                       unsigned int dest = logConsole,\n                       const char* pDir  = 0);\n\n    bool    StartLog();\n    void    StopLog();\n    bool    Update();\n\n    Logger* NullLog()  {  return  &nullLog_;  }\n    bool    IsShutdown() const { return shutdown_; }\n\nprivate:\n    LogManager();\n\n    Logger  nullLog_;\n\n    std::mutex          logsMutex_;\n    std::set<Logger* >  logs_;\n    \n\n    class LogThread\n    {\n    public:\n        LogThread() : alive_(false) {}\n        \n        void  SetAlive() { alive_ = true; }\n        bool  IsAlive() const { return alive_; }\n        void  Stop() {  alive_ = false; }\n        \n        void  Run();\n    private:\n        std::atomic<bool> alive_;\n    };\n    \n    std::shared_ptr<LogThread>  logThread_;\n    std::atomic<bool> shutdown_;\n};\n\nextern __thread Logger*  g_log;\nextern __thread unsigned g_logLevel;\nextern __thread unsigned g_logDest;\n\n#undef INF\n#undef DBG\n#undef WRN\n#undef ERR\n#undef USR\n\n#define  LOG_DBG(x)      (LogHelper(logDEBUG))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logDEBUG)\n#define  LOG_INF(x)      (LogHelper(logINFO))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logINFO)\n#define  LOG_WRN(x)      (LogHelper(logWARN))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logWARN)\n#define  LOG_ERR(x)      (LogHelper(logERROR))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logERROR)\n#define  LOG_USR(x)      (LogHelper(logUSR))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logUSR)\n\n#define  DBG      LOG_DBG(g_log)\n#define  INF      LOG_INF(g_log)\n#define  WRN      LOG_WRN(g_log)\n#define  ERR      LOG_ERR(g_log)\n#define  USR      LOG_USR(g_log)\n\n#endif\n\n"
  },
  {
    "path": "QBase/Log/MemoryFile.cc",
    "content": "#include <errno.h>\n#include <fcntl.h>\n#include <assert.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n\n#include \"MemoryFile.h\"\n\nusing std::size_t;\n\nstatic const size_t kDefaultSize = 1 * 1024 * 1024;\n\nstatic const int   kInvalidFile = -1;\nstatic char* const kInvalidAddr = reinterpret_cast<char*>(-1);\n\nInputMemoryFile::InputMemoryFile() : file_(kInvalidFile),\n                                     memory_(kInvalidAddr),\n                                     offset_(0),\n                                     size_(0)\n{\n}\n\nInputMemoryFile::~InputMemoryFile()\n{\n    Close();\n}\n\nbool InputMemoryFile::_MapReadOnly()\n{\n    assert (file_ != kInvalidFile);\n    assert (size_ == 0);\n\n    struct stat st;\n    fstat(file_, &st);\n    size_ = st.st_size;\n\n    memory_ = (char* )::mmap(0, size_, PROT_READ, MAP_PRIVATE, file_, 0);\n    return memory_ != kInvalidAddr;\n}\n\nvoid InputMemoryFile::_CheckAvail(std::size_t len)\n{\n    if (offset_ + len > size_)\n    {\n        std::string s(\"Not enough data, require \" + std::to_string(len) + \", only has \" + std::to_string(size_ - offset_));\n        throw std::runtime_error(std::move(s));\n    }\n}\n\nvoid InputMemoryFile::Attach(const char* data, size_t len)\n{\n    memory_ = (char* )data;\n    size_ = len;\n    offset_ = 0;\n    file_ = kInvalidFile;\n}\n\nbool  InputMemoryFile::Open(const char* file)\n{\n    Close();\n\n    file_ = ::open(file, O_RDONLY);\n\n    if (file_ == kInvalidFile)\n    {\n        char err[128];\n        snprintf(err, sizeof err - 1, \"OpenForRead %s failed\\n\", file);\n        return false;\n    }\n\n    offset_ = 0;\n    return _MapReadOnly();\n}\n\nvoid  InputMemoryFile::Close()\n{\n    if (file_ != kInvalidFile)\n    {\n        ::munmap(memory_, size_);\n        ::close(file_);\n\n        file_      = kInvalidFile;\n        size_      = 0;\n        memory_   = kInvalidAddr;\n        offset_    = 0;\n    }\n}\n\nconst char* InputMemoryFile::Read(std::size_t& len)\n{\n    if (size_ <= offset_)\n        return 0;\n\n    if (offset_ + len > size_)\n        len = size_ - offset_;\n\n    return  memory_ + offset_;\n}\n\nvoid InputMemoryFile::Skip(size_t len)\n{\n    _CheckAvail(len);\n    offset_ += len;\n}\n\nbool InputMemoryFile::IsOpen() const\n{\n    return  file_ != kInvalidFile;\n}\n\n\n// OutputMemoryFile\n\nOutputMemoryFile::OutputMemoryFile() : file_(kInvalidFile),\n                                       memory_(kInvalidAddr),\n                                       offset_(0),\n                                       size_(0),\n                                       syncPos_(0)\n{\n}\n\nOutputMemoryFile::~OutputMemoryFile()\n{\n    Close();\n}\n\nvoid OutputMemoryFile::_ExtendFileSize(size_t  size)\n{\n    assert(file_ != kInvalidFile);\n\n    if (size > size_)\n        Truncate(size);\n}\n\nbool  OutputMemoryFile::Open(const std::string&  file, bool bAppend)\n{\n    return Open(file.c_str(), bAppend);\n}\n\nbool  OutputMemoryFile::Open(const char* file, bool bAppend)\n{\n    Close();\n\n    file_ = ::open(file, O_RDWR | O_CREAT | (bAppend ? O_APPEND : 0), 0644);\n\n    if (file_ == kInvalidFile)\n    {\n        char err[128];\n        snprintf(err, sizeof err - 1, \"OpenWriteOnly %s failed\\n\", file);\n        perror(err);\n        return false;\n    }\n    if (bAppend)\n    {\n        struct stat st;\n        fstat(file_, &st);\n        size_ = std::max<decltype(size_)>(st.st_size, kDefaultSize);\n        offset_ = st.st_size;\n    }\n    else\n    {\n        size_ = kDefaultSize;\n        offset_ = 0;\n    }\n\n    ::ftruncate(file_, size_);\n    return _MapWriteOnly();\n}\n\nvoid  OutputMemoryFile::Close()\n{\n    if (file_ != kInvalidFile)\n    {\n        ::munmap(memory_, size_);\n        ::ftruncate(file_, offset_);\n        ::close(file_);\n\n        file_ = kInvalidFile;\n        size_ = 0;\n        memory_ = kInvalidAddr;\n        offset_ = 0;\n        syncPos_ = 0;\n    }\n}\n\nbool    OutputMemoryFile::Sync()\n{\n    if (file_ == kInvalidFile)\n        return false;\n\n    if (syncPos_ >= offset_)\n        return false;\n\n    ::msync(memory_ + syncPos_, offset_ - syncPos_, MS_SYNC);\n    syncPos_ = offset_;\n    \n    return true;\n}\n\nbool OutputMemoryFile::_MapWriteOnly()\n{\n    if (size_ == 0 || file_ == kInvalidFile)\n        return false;\n\n#if 0\n    // codes below cause coredump when file size > 4MB\n    if (m_memory != kInvalidAddr)\n        ::munmap(m_memory, m_size);\n#endif\n    memory_ = (char*)::mmap(0, size_, PROT_WRITE, MAP_SHARED, file_, 0);\n    return (memory_ != kInvalidAddr);\n}\n\nvoid OutputMemoryFile::Truncate(std::size_t  size)\n{\n    if (size == size_)\n        return;\n\n    size_ = size;\n    ::ftruncate(file_, size);\n\n    if (offset_> size_)\n        offset_ = size_;\n\n    _MapWriteOnly();\n}\n\nvoid OutputMemoryFile::TruncateTailZero()\n{\n    if (file_ == kInvalidFile)\n        return;\n\n    size_t tail = size_;\n    while (tail > 0 && memory_[--tail] == '\\0')\n        ;\n\n    ++ tail;\n\n    Truncate(tail);\n}\n\nbool OutputMemoryFile::IsOpen() const\n{\n    return  file_ != kInvalidFile;\n}\n\n// consumer\nvoid OutputMemoryFile::Write(const void* data, size_t len)\n{\n    _AssureSpace(len);\n    assert(memory_);\n\n    ::memcpy(memory_ + offset_, data, len);\n    offset_ += len;\n    assert(offset_ <= size_);\n}\n\nvoid OutputMemoryFile::_AssureSpace(size_t  size)\n{\n    size_t  newSize = size_;\n\n    while (offset_ + size > newSize)\n    {\n        if (newSize == 0)\n            newSize = kDefaultSize;\n        else\n            newSize <<= 1;\n    }\n\n    _ExtendFileSize(newSize);\n}\n\n"
  },
  {
    "path": "QBase/Log/MemoryFile.h",
    "content": "#ifndef BERT_MEMORYFILE_H\n#define BERT_MEMORYFILE_H\n\n#include <string>\n#include <stdexcept>\n\nclass InputMemoryFile\n{\npublic:\n    InputMemoryFile();\n   ~InputMemoryFile();\n\n    bool Open(const char* file);\n    void Close();\n\n    void Attach(const char* data, size_t len);\n\n    const char* Read(std::size_t& len);\n    template <typename T>\n    T Read();\n    void Skip(std::size_t len);\n\n    bool IsOpen() const;\n\nprivate:\n    bool _MapReadOnly();\n    void _CheckAvail(std::size_t len);\n\n    int file_;\n    char* memory_;\n    std::size_t offset_;\n    std::size_t size_;\n};\n\ntemplate <typename T>\ninline T InputMemoryFile::Read()\n{\n    _CheckAvail(sizeof(T));\n\n    T res(*reinterpret_cast<T* >(memory_ + offset_));\n    offset_ += sizeof(T);\n    \n    return res;\n}\n\n\nclass OutputMemoryFile\n{\npublic:\n    OutputMemoryFile();\n   ~OutputMemoryFile();\n\n    bool Open(const std::string& file, bool bAppend = true);\n    bool Open(const char* file, bool bAppend = true);\n    void Close();\n    bool Sync();\n\n    void Truncate(std::size_t size);\n    //!! if process terminated abnormally, erase the trash data\n    //requirement: text file.\n    void TruncateTailZero();\n\n    void Write(const void* data, std::size_t len);\n    template <typename T>\n    void Write(const T& t);\n    \n    std::size_t Offset() const { return offset_; }\n    bool IsOpen() const;\n\nprivate:\n    bool _MapWriteOnly();\n    void _ExtendFileSize(std::size_t size);\n    void _AssureSpace(std::size_t size);\n\n    int file_;\n    char* memory_;\n    std::size_t offset_;\n    std::size_t size_;\n    std::size_t syncPos_;\n};\n\n\ntemplate <typename T>\ninline void OutputMemoryFile::Write(const T& t)\n{\n    this->Write(&t, sizeof t);\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/NetThreadPool.cc",
    "content": "\n#include \"NetThreadPool.h\"\n#include \"StreamSocket.h\"\n#include \"Log/Logger.h\"\n#include \"Timer.h\"\n\n#if defined(__gnu_linux__)\n#include \"EPoller.h\"\n#elif defined(__APPLE__)\n#include \"Kqueue.h\"\n#else\n#error \"Only support osx and linux\"\n#endif\n\n#include <cassert>\n#include <errno.h>\n\n\nnamespace Internal\n{\n\nNetThread::NetThread() : running_(true), newCnt_(0)\n{\n#if defined(__gnu_linux__)\n    poller_.reset(new Epoller);\n#else\n    poller_.reset(new Kqueue);\n#endif\n}\n\nNetThread::~NetThread()\n{\n}\n\nvoid NetThread::AddSocket(PSOCKET task, uint32_t events)\n{\n    std::lock_guard<std::mutex>    guard(mutex_);\n    newTasks_.push_back(std::make_pair(task, events)); \n    ++ newCnt_;\n\n    assert (newCnt_ == static_cast<int>(newTasks_.size()));\n}\n\nvoid NetThread::ModSocket(PSOCKET task, uint32_t events)\n{\n    poller_->ModSocket(task->GetSocket(), events, task.get());\n}\n\nvoid NetThread::RemoveSocket(PSOCKET task, uint32_t events)\n{\n    poller_->DelSocket(task->GetSocket(), events);\n}\n\nvoid NetThread::_TryAddNewTasks()\n{\n    if (newCnt_ > 0 && mutex_.try_lock())\n    { \n        NewTasks  tmp;\n        newTasks_.swap(tmp); \n        newCnt_ = 0; \n        mutex_.unlock();\n\n        auto iter(tmp.begin()),\n             end (tmp.end());\n\n        for (; iter != end; ++ iter) \n            _AddSocket(iter->first, iter->second); \n    }\n}\n\nvoid NetThread::_AddSocket(PSOCKET task, uint32_t events)\n{\n    if (poller_->AddSocket(task->GetSocket(), events, task.get()))\n        tasks_.push_back(task);\n}\n\n//////////////////////////////////\nvoid RecvThread::Run()\n{\n    // init log;\n    g_logLevel = logALL;\n    g_logDest  = logFILE;\n    if (g_logLevel && g_logDest)\n    {\n        g_log = LogManager::Instance().CreateLog(g_logLevel, g_logDest, \"recvthread_log\");\n    }\n\n    std::deque<PSOCKET >::iterator it;\n\n    int loopCount = 0;\n    while (IsAlive())\n    {\n        _TryAddNewTasks();\n\n        if (tasks_.empty())\n        {\n            std::this_thread::sleep_for(std::chrono::microseconds(100));\n            continue;\n        }\n\n        const int nReady = poller_->Poll(firedEvents_, static_cast<int>(tasks_.size()), 1);\n        for (int i = 0; i < nReady; ++ i)\n        {\n            assert (!(firedEvents_[i].events & EventTypeWrite));\n\n            Socket* sock = (Socket* )firedEvents_[i].userdata;\n\n            if (firedEvents_[i].events & EventTypeRead)\n            {\n                if (!sock->OnReadable())\n                {\n                    sock->OnError();\n                }\n            }\n\n            if (firedEvents_[i].events & EventTypeError)\n            {\n                sock->OnError();\n            }\n        }\n        \n        if (nReady == 0)\n            loopCount *= 2;\n        \n        if (++ loopCount < 100000)\n            continue;\n\n        loopCount = 0;\n\n        for (auto it(tasks_.begin());\n             it != tasks_.end();\n             )\n        {\n            if ((*it)->Invalid())\n            {\n                NetThreadPool::Instance().DisableRead(*it);\n                RemoveSocket(*it, EventTypeRead);\n                it = tasks_.erase(it);\n            }\n            else\n            {\n                ++ it;\n            }\n        }\n    }\n}\n\nvoid SendThread::Run( )\n{\n    // init log;\n    g_logLevel = logALL;\n    g_logDest  = logFILE;\n    if (g_logLevel && g_logDest)\n    {\n        g_log = LogManager::Instance().CreateLog(g_logLevel, g_logDest, \"sendthread_log\");\n    }\n    \n    std::deque<PSOCKET >::iterator    it;\n    \n    while (IsAlive())\n    {\n        _TryAddNewTasks();\n\n        size_t  nOut = 0;\n        for (it = tasks_.begin(); it != tasks_.end(); )\n        {\n            Socket::SocketType  type  = (*it)->GetSocketType();\n            Socket*  sock = (*it).get();\n            \n            if (type == Socket::SocketType_Stream)\n            {\n                StreamSocket*  tcpSock = static_cast<StreamSocket* >(sock);\n                if (!tcpSock->Send())\n                    tcpSock->OnError();\n            }\n            \n            if (sock->Invalid())\n            {\n                NetThreadPool::Instance().DisableWrite(*it);\n                RemoveSocket(*it, EventTypeWrite);\n                it = tasks_.erase(it);\n            }\n            else\n            {\n                if (sock->epollOut_)\n                    ++ nOut;\n\n                ++ it;\n            }\n        }\n        \n        if (nOut == 0)\n        {\n            std::this_thread::sleep_for(std::chrono::microseconds(100));\n            continue;\n        }\n\n        const int nReady = poller_->Poll(firedEvents_, static_cast<int>(tasks_.size()), 1);\n        for (int i = 0; i < nReady; ++ i)\n        {\n            Socket* sock = (Socket* )firedEvents_[i].userdata;\n            \n            assert (!(firedEvents_[i].events & EventTypeRead));\n            if (firedEvents_[i].events & EventTypeWrite)\n            {\n                if (!sock->OnWritable())\n                {\n                    sock->OnError();\n                }\n            }\n            \n            if (firedEvents_[i].events & EventTypeError)\n            {\n                sock->OnError();\n            }\n        }\n    }\n}\n\n\nvoid NetThreadPool::StopAllThreads()\n{\n    recvThread_->Stop();\n    recvThread_.reset();\n    sendThread_->Stop();\n    sendThread_.reset();\n\n    INF << \"Stop all recv and send threads\";\n}\n\nbool NetThreadPool::AddSocket(PSOCKET sock, uint32_t  events)\n{\n    if (events & EventTypeRead)\n    {\n        if (!recvThread_)\n            return false;\n\n        recvThread_->AddSocket(sock, EventTypeRead);\n    }\n\n    if (events & EventTypeWrite)\n    {\n        if (!sendThread_)\n            return false;\n    \n        sendThread_->AddSocket(sock, EventTypeWrite);\n    }\n\n    return true;\n}\n\nbool NetThreadPool::StartAllThreads()\n{\n    recvThread_.reset(new RecvThread);\n    sendThread_.reset(new SendThread);\n    \n    ThreadPool::Instance().ExecuteTask(std::bind(&RecvThread::Run, recvThread_));\n    ThreadPool::Instance().ExecuteTask(std::bind(&SendThread::Run, sendThread_));\n\n    return  true;\n}\n    \n\nvoid NetThreadPool::EnableRead(const std::shared_ptr<Socket>& sock)\n{\n    if (recvThread_)\n        recvThread_->ModSocket(sock, EventTypeRead);\n}\n\nvoid NetThreadPool::EnableWrite(const std::shared_ptr<Socket>& sock)\n{\n    if (sendThread_)\n        sendThread_->ModSocket(sock, EventTypeWrite);\n}\n   \nvoid NetThreadPool::DisableRead(const std::shared_ptr<Socket>& sock)\n{\n    if (recvThread_)\n        recvThread_->ModSocket(sock, 0);\n}\n\nvoid NetThreadPool::DisableWrite(const std::shared_ptr<Socket>& sock)\n{\n    if (sendThread_)\n        sendThread_->ModSocket(sock, 0);\n}\n\n}\n\n"
  },
  {
    "path": "QBase/NetThreadPool.h",
    "content": "#ifndef BERT_NETTHREADPOOL_H\n#define BERT_NETTHREADPOOL_H\n\n#include <deque>\n#include <vector>\n#include <unistd.h>\n#include <memory>\n#include <atomic>\n#include <mutex>\n#include \"Poller.h\"\n#include \"Threads/ThreadPool.h\"\n\ninline long GetCpuNum()\n{\n    return  sysconf(_SC_NPROCESSORS_ONLN);\n}\n\nclass   Socket;\ntypedef std::shared_ptr<Socket> PSOCKET;\n\nnamespace Internal\n{\n\n\nclass NetThread\n{\npublic:\n    NetThread();\n    virtual ~NetThread();\n\n    bool IsAlive() const  {  return running_; }\n    void Stop()           {  running_ = false;}\n\n    void AddSocket(PSOCKET , uint32_t event);\n    void ModSocket(PSOCKET , uint32_t event);\n    void RemoveSocket(PSOCKET, uint32_t event);\n\nprotected:\n    std::unique_ptr<Poller>        poller_;\n    std::vector<FiredEvent > firedEvents_;    \n    std::deque<PSOCKET>      tasks_;\n    void  _TryAddNewTasks();\n\nprivate:\n    std::atomic<bool> running_;\n\n    std::mutex   mutex_;\n    typedef std::vector<std::pair<std::shared_ptr<Socket>, uint32_t> >    NewTasks; \n    NewTasks     newTasks_; \n    std::atomic<int> newCnt_;\n    void _AddSocket(PSOCKET , uint32_t  event);\n};\n\nclass RecvThread : public NetThread\n{\npublic:\n    void Run();\n};\n\nclass SendThread : public NetThread\n{\npublic:\n    void Run();\n};\n\n\n///////////////////////////////////////////////\nclass NetThreadPool\n{\n    std::shared_ptr<RecvThread> recvThread_;\n    std::shared_ptr<SendThread> sendThread_;\n\npublic:\n    NetThreadPool() = default;\n\n    NetThreadPool(const NetThreadPool& ) = delete;\n    void operator= (const NetThreadPool& ) = delete;\n\n    bool AddSocket(PSOCKET , uint32_t event);\n    bool StartAllThreads();\n    void StopAllThreads();\n    \n    void EnableRead(const std::shared_ptr<Socket>& sock);\n    void EnableWrite(const std::shared_ptr<Socket>& sock);\n    void DisableRead(const std::shared_ptr<Socket>& sock);\n    void DisableWrite(const std::shared_ptr<Socket>& sock);\n\n    static NetThreadPool& Instance()\n    {\n        static  NetThreadPool    pool;\n        return  pool;\n    }\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/Poller.h",
    "content": "#ifndef BERT_POLLER_H\n#define BERT_POLLER_H\n\n#include <vector>\n\nenum EventType\n{\n    EventTypeRead  = 0x1,\n    EventTypeWrite = 0x1 << 1,\n    EventTypeError = 0x1 << 2,\n};\n\nstruct FiredEvent\n{\n    int   events;\n    void* userdata;\n\n    FiredEvent() : events(0), userdata(0)\n    {\n    }\n};\n\nclass Poller\n{\npublic:\n    Poller() : multiplexer_(-1)\n    {\n    }\n\n    virtual ~Poller()\n    {\n    }\n\n    virtual bool AddSocket(int sock, int events, void* userPtr) = 0;\n    virtual bool ModSocket(int sock, int events, void* userPtr) = 0;\n    virtual bool DelSocket(int sock, int events) = 0;\n\n    virtual int Poll(std::vector<FiredEvent>& events, std::size_t maxEv, int timeoutMs) = 0;\n\nprotected:\n    int  multiplexer_;\n};\n\n#endif\n\n"
  },
  {
    "path": "QBase/README.md",
    "content": "# QBase\n\nQBase最初是C++03代码，由于历史原因，再加上它也极其稳定，我不打算替换了。\n\n感兴趣的可以看我的另外一个最新后台开发基础库[ananas](https://github.com/loveyacper/ananas)\n"
  },
  {
    "path": "QBase/Server.cc",
    "content": "#include <signal.h>\n#include <ctime>\n#include <cstdlib>\n#include <cstring>\n#include <sys/resource.h>\n#include \"Server.h\"\n#include \"ListenSocket.h\"\n#include \"ClientSocket.h\"\n#include \"StreamSocket.h\"\n#include \"NetThreadPool.h\"\n#include \"Timer.h\"\n#include \"Log/Logger.h\"\n\nusing Internal::NetThreadPool;\n\n\nvoid Server::IntHandler(int signum)\n{\n    if (Server::Instance() != NULL)\n        Server::Instance()->Terminate();\n}\n\nvoid Server::HupHandler(int signum)\n{\n    if (Server::Instance() != NULL)\n        Server::Instance()->reloadCfg_ = true;\n}\n\nServer* Server::sinstance_ = nullptr;\n\nstd::set<int> Server::slistenSocks_;\n\nServer::Server() : bTerminate_(false), reloadCfg_(false)\n{\n    if (sinstance_ == NULL)\n        sinstance_ = this;\n    else\n        ::abort();\n}\n\nServer::~Server()\n{\n    sinstance_ = NULL;\n}\n\nbool Server::_RunLogic() \n{\n    return tasks_.DoMsgParse();\n}\n\nbool Server::TCPBind(const SocketAddr& addr, int tag)\n{\n    using Internal::ListenSocket;\n\n    auto s(std::make_shared<ListenSocket>(tag));\n\n    if (s->Bind(addr))\n    {\n        slistenSocks_.insert(s->GetSocket());\n        return true;\n    }\n    \n    return false;\n}\n\n\nvoid Server::TCPReconnect(const SocketAddr& peer, int tag)\n{\n    INF << __FUNCTION__ << peer.GetIP();\n\n    Timer* pTimer = TimerManager::Instance().CreateTimer();\n    pTimer->Init(2 * 1000, 1);\n    pTimer->SetCallback([&, tag, peer]() {\n        USR << \"OnTimer reconnect to \" << peer.GetIP() << \":\" << peer.GetPort();\n        Server::Instance()->TCPConnect(peer, [=]() { Server::Instance()->TCPReconnect(peer, tag); }, tag);\n    });\n\n\n    TimerManager::Instance().AsyncAddTimer(pTimer);\n}\n\nvoid Server::TCPConnect(const SocketAddr& peer, int tag)\n{\n    _TCPConnect(peer, nullptr, tag);\n}\n\nvoid Server::TCPConnect(const SocketAddr& peer, const std::function<void ()>& cb, int tag)\n{\n    _TCPConnect(peer, &cb, tag);\n}\n\nvoid Server::_TCPConnect(const SocketAddr& peer, const std::function<void ()>* cb, int tag)\n{\n    auto client(std::make_shared<ClientSocket>(tag));\n    if (cb)\n        client->SetFailCallback(*cb);\n\n    client->Connect(peer);\n}\n\nvoid Server::MainLoop(bool daemon)\n{\n    struct sigaction sig;\n    ::memset(&sig, 0, sizeof(sig));\n    sig.sa_handler = &Server::IntHandler;\n    sigaction(SIGINT, &sig, NULL);\n    sigaction(SIGQUIT, &sig, NULL);\n    sigaction(SIGABRT, &sig, NULL);\n    sigaction(SIGTERM, &sig, NULL);\n    sig.sa_handler =  &Server::HupHandler;\n    sigaction(SIGHUP, &sig, NULL);\n\n    sig.sa_handler = SIG_IGN;\n    sigaction(SIGPIPE, &sig, NULL);\n\n    ::pthread_atfork(nullptr, nullptr, AtForkHandler);\n    \n    ::srand(static_cast<unsigned int>(time(NULL)));\n    ::srandom(static_cast<unsigned int>(time(NULL)));\n\n    // daemon must be first, before descriptor open, threads create\n    if (daemon)\n    {\n        ::daemon(1, 0);\n    }\n    \n    if (NetThreadPool::Instance().StartAllThreads() &&\n        _Init() &&\n        LogManager::Instance().StartLog())\n    {\n        while (!bTerminate_)\n        {\n            if (reloadCfg_)\n            {\n                ReloadConfig();\n                reloadCfg_ = false;\n            }\n\n            if (!_RunLogic())\n                std::this_thread::sleep_for(std::chrono::microseconds(100));\n        }\n    }\n\n    tasks_.Clear();\n    _Recycle();\n    NetThreadPool::Instance().StopAllThreads();\n    LogManager::Instance().StopLog();\n    \n    ThreadPool::Instance().JoinAll();\n}\n\n\nstd::shared_ptr<StreamSocket> Server::_OnNewConnection(int tcpsock, int tag)\n{\n    WRN << \"implement your tcp accept, now close socket \" << tcpsock;\n    return std::shared_ptr<StreamSocket>(nullptr);\n}\n\nvoid Server::NewConnection(int sock, int tag, const std::function<void ()>& cb)\n{\n    if (sock == INVALID_SOCKET)\n        return;\n\n    auto conn = _OnNewConnection(sock, tag);\n    \n    if (!conn)\n    {\n        Socket::CloseSocket(sock);\n        return;\n    }\n\n    conn->SetOnDisconnect(cb);\n\n    if (NetThreadPool::Instance().AddSocket(conn, EventTypeRead | EventTypeWrite))\n        tasks_.AddTask(conn);\n}\n\nvoid Server::AtForkHandler()\n{\n    for (auto sock : slistenSocks_)\n    {\n        close(sock);\n    }\n}\n\nvoid Server::DelListenSock(int sock)\n{\n    if (sock == INVALID_SOCKET)\n        return;\n    \n    auto n = slistenSocks_.erase(sock);\n\n    if (n != 1)\n        ERR << \"Failed DelListenSock \" << sock;\n    else\n        INF << \"Success DelListenSock \" << sock;\n}\n"
  },
  {
    "path": "QBase/Server.h",
    "content": "\n#ifndef BERT_SERVER_H\n#define BERT_SERVER_H\n\n#include <set>\n#include <functional>\n#include \"TaskManager.h\"\n\nstruct SocketAddr;\n\nclass Server    // Should singleton\n{\nprotected:\n    virtual bool _RunLogic();\n    virtual void _Recycle() { }\n    virtual bool _Init() = 0;\n    \n    Server();\n\n    Server(const Server& ) = delete;\n    void operator= (const Server& ) = delete;\n    \npublic:\n    virtual ~Server();\n\n    bool  TCPBind(const SocketAddr& listenAddr, int tag);\n    void  TCPReconnect(const SocketAddr& peer, int tag);\n\n    static Server*  Instance() {   return   sinstance_;  }\n\n    bool IsTerminate() const { return bTerminate_; }\n    void Terminate()  { bTerminate_ = true; }\n\n    void MainLoop(bool daemon = false);\n    void NewConnection(int sock, int tag, const std::function<void ()>& cb = std::function<void ()>());\n\n    void TCPConnect(const SocketAddr& peer, int tag);\n    void TCPConnect(const SocketAddr& peer, const std::function<void ()>& cb, int tag);\n\n    size_t  TCPSize() const  {  return  tasks_.TCPSize(); }\n\n    // SIGHUP handler, in fact, you should load config use this function;\n    virtual void ReloadConfig()    { }\n\n    static void IntHandler(int sig);\n    static void HupHandler(int sig);\n\n    std::shared_ptr<StreamSocket>  FindTCP(unsigned int id) const { return tasks_.FindTCP(id); }\n    \n    static void AtForkHandler();\n    static void DelListenSock(int sock);\n\nprivate:\n    virtual std::shared_ptr<StreamSocket> _OnNewConnection(int tcpsock, int tag);\n    void _TCPConnect(const SocketAddr& peer, const std::function<void()>* cb = nullptr, int tag = -1);\n\n    std::atomic<bool> bTerminate_;\n    Internal::TaskManager   tasks_;\n    bool          reloadCfg_;\n    static Server*   sinstance_;\n    \n    static std::set<int>  slistenSocks_;\n};\n\n#endif\n\n"
  },
  {
    "path": "QBase/Socket.cc",
    "content": "\n#include <cassert>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/socket.h>\n#include <netinet/tcp.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n\n#if defined(__APPLE__)\n#include <unistd.h>\n#endif\n\n#include \"Socket.h\"\n#include \"NetThreadPool.h\"\n#include \"Log/Logger.h\"\n\nstd::atomic<std::size_t> Socket::sid_;\n\nSocket::Socket() : localSock_(INVALID_SOCKET),\n                   epollOut_(false),\n                   invalid_(false)\n{\n    ++ sid_;\n    \n    std::size_t expect = 0;\n    sid_.compare_exchange_strong(expect, 1);\n                   \n    id_ = sid_;\n}\n\nSocket::~Socket()\n{\n    CloseSocket(localSock_);\n}\n\nint Socket::CreateUDPSocket()\n{\n    return ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);\n}\n\nbool Socket::OnError()   \n{\n    bool expect = false;\n    if (invalid_.compare_exchange_strong(expect, true))\n    {\n        return true;\n    }\n\n    return false;\n}\n\nint Socket::CreateTCPSocket()\n{\n    return ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n}\n\nvoid Socket::CloseSocket(int& sock)\n{\n    if (sock != INVALID_SOCKET)\n    {\n        ::shutdown(sock, SHUT_RDWR);\n        ::close(sock);\n        DBG << \"CloseSocket \" << sock;\n        sock = INVALID_SOCKET;\n    }\n}\n\nvoid  Socket::SetNonBlock(int sock, bool nonblock)\n{\n    int flag = ::fcntl(sock, F_GETFL, 0); \n    assert(flag >= 0 && \"Non Block failed\");\n\n    if (nonblock)\n        flag = ::fcntl(sock, F_SETFL, flag | O_NONBLOCK);\n    else\n        flag = ::fcntl(sock, F_SETFL, flag & ~O_NONBLOCK);\n    \n}\n\nvoid Socket::SetNodelay(int sock)\n{\n    int nodelay = 1;\n    ::setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(int));\n}\n\nvoid Socket::SetSndBuf(int sock, socklen_t winsize)\n{\n    ::setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&winsize, sizeof(winsize));\n}\n\nvoid Socket::SetRcvBuf(int sock, socklen_t winsize)\n{\n    ::setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&winsize, sizeof(winsize));\n}\n\nvoid Socket::SetReuseAddr(int sock)\n{\n    int reuse = 1;\n    ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));\n}\n\nbool Socket::GetLocalAddr(int  sock, SocketAddr& addr)\n{\n    sockaddr_in localAddr;\n    socklen_t   len = sizeof(localAddr);\n\n    if (0 == ::getsockname(sock, (struct sockaddr*)&localAddr, &len))\n    {\n        addr.Init(localAddr);\n    }\n    else\n    {\n        return  false;\n    }\n\n    return  true;\n}\n\nbool  Socket::GetPeerAddr(int  sock, SocketAddr& addr)\n{\n    sockaddr_in  remoteAddr;\n    socklen_t    len = sizeof(remoteAddr);\n    if (0 == ::getpeername(sock, (struct sockaddr*)&remoteAddr, &len))\n    {\n        addr.Init(remoteAddr);\n    }\n    else\n    {\n        return  false;\n    }\n\n    return  true;\n}\n\nvoid Socket::GetMyAddrInfo(unsigned int* addrs, int num)\n{\n    char buff[BUFSIZ];\n    struct ifconf conf;\n    conf.ifc_len = BUFSIZ;\n    conf.ifc_buf = buff;\n\n    int sock = CreateUDPSocket();\n    ioctl(sock, SIOCGIFCONF, &conf);\n    int maxnum  = conf.ifc_len / sizeof(struct ifreq);\n    struct ifreq* ifr = conf.ifc_req;\n\n    int cnt = 0;\n    for(int i=0; cnt < num && i < maxnum;i++)\n    {\n        if (NULL == ifr)\n            break;\n\n        ioctl(sock, SIOCGIFFLAGS, ifr);\n\n        if(((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP))\n        {\n            struct sockaddr_in *pAddr = (struct sockaddr_in *)(&ifr->ifr_addr);\n            addrs[cnt ++] = pAddr->sin_addr.s_addr;\n        }\n        ++ ifr;\n    }\n\n    for ( ; cnt < num; ++ cnt)\n    {\n        addrs[cnt] = 0;\n    }\n\n    close(sock);\n}\n\n\n"
  },
  {
    "path": "QBase/Socket.h",
    "content": "\n#ifndef BERT_SOCKET_H\n#define BERT_SOCKET_H\n\n#include <arpa/inet.h>\n#include <string.h>\n#include <string>\n#include <memory>\n#include <atomic>\n#include <functional>\n\n#define INVALID_SOCKET (int)(~0)\n#define SOCKET_ERROR (-1)\n#define INVALID_PORT (-1)\n\n\nstruct SocketAddr\n{\n    SocketAddr()\n    {\n        Clear();\n    }\n    \n    SocketAddr(const SocketAddr& other)\n    {\n        memcpy(&addr_, &other.addr_, sizeof addr_);\n    }\n\n    SocketAddr& operator= (const SocketAddr&  other)\n    {\n        if (this != &other)\n            memcpy(&addr_, &other.addr_, sizeof addr_);\n\n        return *this;\n    }\n\n    SocketAddr(const sockaddr_in& addr)\n    {\n        Init(addr);\n    }\n\n    // ip port format:  127.0.0.1:6379\n    SocketAddr(const std::string& ipport)\n    {\n        std::string::size_type p = ipport.find_first_of(':');\n        std::string ip = ipport.substr(0, p);\n        std::string port = ipport.substr(p + 1);\n\n        Init(ip.c_str(), static_cast<uint16_t>(std::stoi(port)));\n    }\n\n    SocketAddr(uint32_t  netip, uint16_t netport)\n    {\n        Init(netip, netport);\n    }\n\n    SocketAddr(const char* ip, uint16_t hostport)\n    {\n        Init(ip, hostport);\n    }\n\n    void Init(const sockaddr_in& addr)\n    {\n        memcpy(&addr_, &addr, sizeof(addr));\n    }\n\n    void Init(uint32_t netip, uint16_t netport)\n    {\n        addr_.sin_family = AF_INET;       \n        addr_.sin_addr.s_addr = netip;       \n        addr_.sin_port = netport;\n    }\n\n    void Init(const char* ip, uint16_t hostport)\n    {\n        addr_.sin_family = AF_INET;\n        addr_.sin_addr.s_addr = ::inet_addr(ip);\n        addr_.sin_port = htons(hostport);\n    }\n\n    const sockaddr_in& GetAddr() const\n    {\n        return addr_;\n    }\n\n    const char* GetIP() const\n    {\n        return ::inet_ntoa(addr_.sin_addr);\n    }\n    \n    const char* GetIP(char* buf, socklen_t size) const\n    {\n        return ::inet_ntop(AF_INET, (const char*)&addr_.sin_addr, buf, size);\n    }\n\n    unsigned short GetPort() const\n    {\n        return ntohs(addr_.sin_port);\n    }\n    \n    std::string ToString() const\n    {\n        char tmp[32];\n        const char* res = inet_ntop(AF_INET,\n                                    &addr_.sin_addr,\n                                    tmp,\n                                    (socklen_t)(sizeof tmp));\n\n        return std::string(res) + \":\" + std::to_string(ntohs(addr_.sin_port));\n    }\n\n    bool  Empty() const { return  0 == addr_.sin_family; }\n    void  Clear()       { memset(&addr_, 0, sizeof addr_); }\n    \n    inline friend bool operator== (const SocketAddr& a, const SocketAddr& b)\n    {\n        return a.addr_.sin_family      ==  b.addr_.sin_family &&\n               a.addr_.sin_addr.s_addr ==  b.addr_.sin_addr.s_addr &&\n               a.addr_.sin_port        ==  b.addr_.sin_port ;\n    }\n    \n    inline friend bool operator!= (const SocketAddr& a, const SocketAddr& b)\n    {\n        return !(a == b);\n    }\n\n    sockaddr_in addr_;\n};\n\n// custom specialization of std::hash can be injected into std\nnamespace std\n{ \n    template<>\n    struct hash<SocketAddr> \n    { \n        typedef SocketAddr argument_type; \n        typedef std::size_t result_type; \n        result_type operator()(const argument_type& s) const noexcept \n        {\n            result_type h1 = std::hash<short>{}(s.addr_.sin_family); \n            result_type h2 = std::hash<unsigned short>{}(s.addr_.sin_port); \n            result_type h3 = std::hash<unsigned int>{}(s.addr_.sin_addr.s_addr);\n            result_type tmp = h1 ^ (h2 << 1);\n            return h3 ^ (tmp << 1);\n        }\n    }; \n}\n\n\nnamespace Internal\n{\nclass SendThread;\n}\n\n// Abstraction for a TCP socket\nclass Socket : public std::enable_shared_from_this<Socket>\n{\n    friend class Internal::SendThread;\n\npublic:\n    virtual ~Socket();\n    \n    Socket(const Socket& ) = delete;\n    void operator= (const Socket& ) = delete;\n\n    enum SocketType\n    {\n        SocketType_Invalid= -1,\n        SocketType_Listen,\n        SocketType_Client,\n        SocketType_Stream,\n    };\n\n    virtual SocketType GetSocketType() const { return SocketType_Invalid; }\n    bool Invalid() const { return invalid_; }\n    \n    int  GetSocket() const { return localSock_; }\n    std::size_t GetID() const { return id_; }\n\n    virtual bool OnReadable() { return false; }\n    virtual bool OnWritable() { return false; }\n    virtual bool OnError();\n    virtual void OnConnect()  { }\n    virtual void OnDisconnect()  { }\n\n    static int CreateTCPSocket();\n    static int CreateUDPSocket();\n    static void CloseSocket(int &sock);\n    static void SetNonBlock(int sock, bool nonBlock = true);\n    static void SetNodelay(int sock);\n    static void SetSndBuf(int sock, socklen_t size = 128 * 1024);\n    static void SetRcvBuf(int sock, socklen_t size = 128 * 1024);\n    static void SetReuseAddr(int sock);\n    static bool GetLocalAddr(int sock, SocketAddr& );\n    static bool GetPeerAddr(int sock,  SocketAddr& );\n    static void GetMyAddrInfo(unsigned int* addrs, int num);\n\nprotected:\n    Socket();\n\n    // The local socket\n    int localSock_;\n    bool epollOut_;\n\nprivate:\n    std::atomic<bool> invalid_;\n    std::size_t id_;\n    static std::atomic<std::size_t> sid_;\n};\n\n\n#endif\n\n"
  },
  {
    "path": "QBase/StreamSocket.cc",
    "content": "#include <errno.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netinet/tcp.h>\n\n#include \"StreamSocket.h\"\n#include \"Server.h\"\n#include \"NetThreadPool.h\"\n#include \"Log/Logger.h\"\n\nusing std::size_t;\n\nStreamSocket::StreamSocket()\n{\n}\n\nStreamSocket::~StreamSocket()\n{\n    INF << __FUNCTION__ << \" peer (\"\n        << peerAddr_.ToString()\n        << \")\";\n}\n\nbool StreamSocket::Init(int fd, const SocketAddr& peer)\n{\n    if (fd < 0)\n        return false;\n\n    peerAddr_ = peer;\n    localSock_ = fd;\n    SetNonBlock(localSock_);\n\n    INF << __FUNCTION__ << \" peer address (\"\n        << peerAddr_.ToString()\n        << \"), local socket is \" << fd;\n    \n#if defined(__APPLE__)\n    int set = 1;\n    setsockopt(localSock_, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));\n#endif\n\n    return  true;\n}\n\nint StreamSocket::Recv()\n{\n    if (recvBuf_.Capacity() == 0)\n    {\n        recvBuf_.InitCapacity(64 * 1024); // First recv data, allocate buffer\n    }\n    \n    BufferSequence  buffers;\n    recvBuf_.GetSpace(buffers);\n    if (buffers.count == 0)\n    {\n        WRN << \"Recv buffer is full\";\n        return 0;\n    }\n\n    int ret = static_cast<int>(::readv(localSock_, buffers.buffers, static_cast<int>(buffers.count)));\n    if (ret == ERRORSOCKET && (EAGAIN == errno || EWOULDBLOCK == errno))\n        return 0;\n\n    if (ret > 0)\n        recvBuf_.AdjustWritePtr(ret);\n\n    return (0 == ret) ? EOFSOCKET : ret;\n}\n\n\nint StreamSocket::_Send(const BufferSequence& bf)\n{\n    auto  total = bf.TotalBytes();\n    if (total == 0)\n        return 0;\n\n    int ret = static_cast<int>(::writev(localSock_, bf.buffers, static_cast<int>(bf.count)));\n    if (ERRORSOCKET == ret && (EAGAIN == errno || EWOULDBLOCK == errno))\n    {\n        epollOut_ = true;\n        ret = 0;\n    }\n    else if (ret > 0 && static_cast<size_t>(ret) < total)\n    {\n        epollOut_ = true;\n    }\n    else if (static_cast<size_t>(ret) == total)\n    {\n        epollOut_ = false;\n    }\n\n    return ret;\n}\n\n\nbool StreamSocket::SendPacket(const void* data, size_t bytes)\n{\n    if (data && bytes > 0)\n        sendBuf_.Write(data, bytes);\n\n    return true;\n}\n\nbool StreamSocket::SendPacket(Buffer& bf)\n{\n    return SendPacket(bf.ReadAddr(), bf.ReadableSize());\n}\n\n\nbool StreamSocket::SendPacket(AttachedBuffer& af)\n{\n    return SendPacket(af.ReadAddr(), af.ReadableSize());\n}\n\nbool StreamSocket::SendPacket(qedis::UnboundedBuffer& ubf)\n{\n    return SendPacket(ubf.ReadAddr(), ubf.ReadableSize());\n}\n\nbool StreamSocket::OnReadable()\n{\n    int nBytes = StreamSocket::Recv();\n    if (nBytes < 0)\n    {\n        INF << __FUNCTION__ << \" failed, peer address (\"\n            << peerAddr_.ToString()\n            << \"), local socket is \"\n            << localSock_;\n        \n        Internal::NetThreadPool::Instance().DisableRead(shared_from_this());\n        return false;\n    }\n\n    return true;\n}\n\nbool StreamSocket::Send()\n{\n    if (epollOut_)\n        return true;\n\n    BufferSequence  bf;\n    sendBuf_.ProcessBuffer(bf);\n    \n    size_t  total = bf.TotalBytes();\n    if (total == 0)  return true;\n    \n    int  nSent = _Send(bf);\n    \n    if (nSent > 0)\n    {\n        sendBuf_.Skip(nSent);\n    }\n        \n    if (epollOut_)\n    {\n        Internal::NetThreadPool::Instance().EnableWrite(shared_from_this());\n        INF << __FUNCTION__ << \" peer address (\"\n            << peerAddr_.ToString()\n            << \"), local socket is \"\n            << localSock_\n            << \", register write event\";\n    }\n    \n    return  nSent >= 0;\n}\n\n// drive by EPOLLOUT\nbool StreamSocket::OnWritable()\n{\n    BufferSequence  bf;\n    sendBuf_.ProcessBuffer(bf);\n    \n    size_t  total = bf.TotalBytes();\n    int     nSent = 0;\n    if (total > 0)\n    {\n        nSent = _Send(bf);\n        if (nSent > 0)\n            sendBuf_.Skip(nSent);\n    }\n    else\n    {\n        epollOut_ = false;\n    }\n\n    if (!epollOut_)\n    {\n        INF << __FUNCTION__ << \" peer address (\"\n            << peerAddr_.ToString()\n            << \"), local socket is \"\n            << localSock_\n            << \", unregister write event\";\n        Internal::NetThreadPool::Instance().DisableWrite(shared_from_this());\n    }\n\n    return  nSent >= 0;\n}\n\nbool StreamSocket::OnError()\n{\n    if (Socket::OnError())\n    {\n        ERR << __FUNCTION__ << \" peer address (\"\n            << peerAddr_.ToString()\n            << \"), local socket is \"\n            << localSock_;\n\n        if (onDisconnect_)\n            onDisconnect_();\n\n        return true;\n    }\n        \n    return false;\n}\n\nbool StreamSocket::DoMsgParse()\n{\n    bool busy = false;\n    while (!recvBuf_.IsEmpty())\n    {\n        BufferSequence  datum;\n        recvBuf_.GetDatum(datum, recvBuf_.ReadableSize());\n\n        AttachedBuffer af(datum);\n        auto  bodyLen = _HandlePacket(af.ReadAddr(), af.ReadableSize());\n        if (bodyLen > 0)\n        {\n            busy = true;\n            recvBuf_.AdjustReadPtr(bodyLen);\n        }\n        else\n        {\n            break;\n        }\n    }\n\n    return  busy;\n}\n\n"
  },
  {
    "path": "QBase/StreamSocket.h",
    "content": "\n#ifndef BERT_STREAMSOCKET_H\n#define BERT_STREAMSOCKET_H\n\n#include \"AsyncBuffer.h\"\n#include \"Socket.h\"\n#include <sys/types.h>\n#include <sys/socket.h>\n\nusing PacketLength = int32_t;\n\n// Abstraction for a TCP connection\nclass StreamSocket : public Socket\n{\n    friend class SendThread;\npublic:\n    StreamSocket();\n   ~StreamSocket();\n\n    bool       Init(int localfd, const SocketAddr& peer);\n    SocketType GetSocketType() const { return SocketType_Stream; }\n\npublic:\n    // Receive data\n    int    Recv();\npublic:\n    // Send data\n    bool   SendPacket(const void* , std::size_t );\n    bool   SendPacket(Buffer&  bf);\n    bool   SendPacket(AttachedBuffer& abf);\n    bool   SendPacket(qedis::UnboundedBuffer& ubf);\n    template <int N>\n    bool   SendPacket(StackBuffer<N>&  sb);\n\n    bool   OnReadable();\n    bool   OnWritable();\n    bool   OnError();\n\n    bool  DoMsgParse(); // false if no msg\n\n    void  SetOnDisconnect(const std::function<void ()>& cb = std::function<void ()>()) { onDisconnect_ = cb; }\n    \n    // send thread\n    bool  Send();\n    \n    const SocketAddr& GetPeerAddr() const { return peerAddr_; }\n\nprotected:\n    SocketAddr  peerAddr_;\n\nprivate:\n    std::function<void ()> onDisconnect_;\n\n    int    _Send(const BufferSequence& bf);\n    virtual PacketLength _HandlePacket(const char* msg, std::size_t len) = 0;\n\n    // For human readability\n    enum\n    {\n        TIMEOUTSOCKET =  0,\n        ERRORSOCKET   = -1,\n        EOFSOCKET     = -2,\n    };\n\n    Buffer recvBuf_;\n    AsyncBuffer sendBuf_;\n};\n\ntemplate <int N>\ninline bool  StreamSocket::SendPacket(StackBuffer<N>& sf)\n{\n    return  SendPacket(sf.ReadAddr(), sf.ReadableSize());\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/TaskManager.cc",
    "content": "#include <cassert>\n#include \"TaskManager.h\"\n#include \"StreamSocket.h\"\n#include \"Log/Logger.h\"\n\nnamespace Internal\n{\n\nTaskManager::~TaskManager()\n{\n    assert(Empty() && \"Why you do not clear container before exit?\");\n}\n    \n     \nbool TaskManager::AddTask(PTCPSOCKET task)\n{   \n    std::lock_guard<std::mutex> guard(lock_);\n    newTasks_.push_back(task);\n    ++ newCnt_;\n\n    return true;\n}\n\nTaskManager::PTCPSOCKET TaskManager::FindTCP(unsigned int id) const\n{\n    if (id > 0)\n    {\n        auto it = tcpSockets_.find(id);\n        if (it != tcpSockets_.end())\n            return it->second;\n    }\n           \n    return PTCPSOCKET();\n}\n\nbool TaskManager::_AddTask(PTCPSOCKET task)\n{   \n    //bool succ = tcpSockets_.insert(std::map<int, PTCPSOCKET>::value_type(task->GetID(), task)).second;\n    bool succ = tcpSockets_.insert({task->GetID(), task}).second;\n    return succ;    \n}\n\n\nvoid TaskManager::_RemoveTask(std::map<int, PTCPSOCKET>::iterator& it)    \n{\n    tcpSockets_.erase(it ++);\n}\n\n\nbool TaskManager::DoMsgParse()\n{\n    if (newCnt_ > 0 && lock_.try_lock())\n    {\n        NEWTASKS_T tmpNewTask;\n        tmpNewTask.swap(newTasks_);\n        newCnt_ = 0;\n        lock_.unlock();\n\n        for (const auto& task : tmpNewTask)\n        {\n            if (!_AddTask(task))\n            {\n                ERR << \"Why can not insert tcp socket \"\n                    << task->GetSocket()\n                    << \", id = \"\n                    << task->GetID();\n            }\n            else\n            {\n                INF << \"New connection from \"\n                    << task->GetPeerAddr().ToString()\n                    << \", id = \"\n                    << task->GetID();\n\n                task->OnConnect();\n            }\n        }\n    }\n\n    bool busy = false;\n\n    for (auto it(tcpSockets_.begin()); it != tcpSockets_.end(); )\n    {\n        if (!it->second || it->second->Invalid())\n        {\n            if (it->second)\n            {\n                INF << \"Close connection from \"\n                    << it->second->GetPeerAddr().ToString()\n                    << \", id = \"\n                    << it->second->GetID();\n\n                it->second->OnDisconnect();\n            }\n            _RemoveTask(it);\n        }\n        else\n        {\n            if (it->second->DoMsgParse() && !busy)\n                busy = true;\n\n            ++ it;\n        }\n    }\n\n    return busy;\n}\n\n}\n\n"
  },
  {
    "path": "QBase/TaskManager.h",
    "content": "#ifndef BERT_TASKMANAGER_H\n#define BERT_TASKMANAGER_H\n\n#include <vector>\n#include <map>\n#include <mutex>\n#include <memory>\n#include <atomic>\n\nclass StreamSocket;\n\nnamespace Internal\n{\n\nclass TaskManager \n{\n    typedef std::shared_ptr<StreamSocket>     PTCPSOCKET;\n    typedef std::vector<PTCPSOCKET>     NEWTASKS_T;\n\npublic:\n    TaskManager() : newCnt_(0) { }\n    ~TaskManager();\n    \n    bool AddTask(PTCPSOCKET );\n\n    bool Empty() const { return tcpSockets_.empty(); }\n    void Clear()  { tcpSockets_.clear(); }\n    PTCPSOCKET  FindTCP(unsigned int id) const;\n    \n    size_t TCPSize() const  {  return  tcpSockets_.size(); }\n\n    bool DoMsgParse();\n\nprivate:\n    bool _AddTask(PTCPSOCKET task);\n    void _RemoveTask(std::map<int, PTCPSOCKET>::iterator& );\n    std::map<int, PTCPSOCKET>  tcpSockets_;\n\n    // Lock for new tasks\n    std::mutex      lock_;\n    NEWTASKS_T      newTasks_; \n    std::atomic<int> newCnt_; // vector::empty() is not thread-safe !!!\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/Threads/ThreadPool.cc",
    "content": "#include \"ThreadPool.h\"\n\n__thread bool ThreadPool::working_ = true;\n\nThreadPool::ThreadPool() : waiters_(0), shutdown_(false)\n{\n    monitor_ = std::thread([this]() { this->_MonitorRoutine(); } );\n    maxIdleThread_ = std::max(1U, std::thread::hardware_concurrency());\n    pendingStopSignal_ = 0;\n}\n\nThreadPool::~ThreadPool()\n{\n    JoinAll();\n}\n\nThreadPool& ThreadPool::Instance()\n{\n    static ThreadPool  pool;\n    return pool;\n}\n\nvoid    ThreadPool::SetMaxIdleThread(unsigned int m)\n{\n    if (0 < m && m <= kMaxThreads)\n        maxIdleThread_ = m;\n}\n\nvoid    ThreadPool::JoinAll()\n{\n    decltype(workers_)  tmp;\n    \n    {\n        std::unique_lock<std::mutex>  guard(mutex_);\n        if (shutdown_)\n            return;\n        \n        shutdown_ = true;\n        cond_.notify_all();\n        \n        tmp.swap(workers_);\n        workers_.clear();\n    }\n    \n    for (auto& t : tmp)\n    {\n        if (t.joinable())\n            t.join();\n    }\n    \n    if (monitor_.joinable())\n        monitor_.join();\n}\n\nvoid   ThreadPool::_CreateWorker()\n{\n    std::thread  t([this]() { this->_WorkerRoutine(); } );\n    workers_.push_back(std::move(t));\n}\n\nvoid   ThreadPool::_WorkerRoutine()\n{\n    working_ = true;\n    \n    while (working_)\n    {\n        std::function<void ()>   task;\n        \n        {\n            std::unique_lock<std::mutex>    guard(mutex_);\n            \n            ++ waiters_;\n            cond_.wait(guard, [this]()->bool { return this->shutdown_ || !tasks_.empty(); } );\n            -- waiters_;\n            \n            if (this->shutdown_ && tasks_.empty())\n                return;\n            \n            task = std::move(tasks_.front());\n            tasks_.pop_front();\n        }\n        \n        task();\n    }\n    \n    // if reach here, this thread is recycled by monitor thread\n    -- pendingStopSignal_;\n}\n\nvoid   ThreadPool::_MonitorRoutine()\n{\n    while (!shutdown_)\n    {\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n        \n        std::unique_lock<std::mutex>   guard(mutex_);\n        if (shutdown_)\n            return;\n        \n        auto nw = waiters_;\n\n        // if there is any pending stop signal to consume waiters\n        nw -= pendingStopSignal_;\n\n        while (nw -- > maxIdleThread_)\n        {\n            tasks_.push_back([this]() { working_ = false; });\n            cond_.notify_one();\n            ++ pendingStopSignal_;\n        }\n    }\n}\n"
  },
  {
    "path": "QBase/Threads/ThreadPool.h",
    "content": "#ifndef BERT_THREADPOOL_H\n#define BERT_THREADPOOL_H\n\n#include <deque>\n#include <functional>\n#include <thread>\n#include <memory>\n#include <future>\n#include <atomic>\n#include <mutex>\n#include <condition_variable>\n\n\nclass ThreadPool final\n{\npublic:\n    ~ThreadPool();\n    \n    ThreadPool(const ThreadPool& ) = delete;\n    void operator=(const ThreadPool& ) = delete;\n    \n    static ThreadPool& Instance();\n    \n    template <typename F, typename... Args>\n    auto    ExecuteTask(F&& f, Args&&... args) -> std::future<typename std::result_of<F (Args...)>::type>;\n    \n    void    JoinAll();\n    void    SetMaxIdleThread(unsigned int m);\n    \nprivate:\n    ThreadPool();\n    \n    void   _CreateWorker();\n    void   _WorkerRoutine();\n    void   _MonitorRoutine();\n    \n    std::thread      monitor_;\n    std::atomic<unsigned> maxIdleThread_;\n    std::atomic<unsigned> pendingStopSignal_;\n    \n    static __thread   bool      working_;\n    std::deque<std::thread>     workers_;\n    \n    std::mutex                  mutex_;\n    std::condition_variable     cond_;\n    unsigned                    waiters_;\n    bool                        shutdown_;\n    std::deque<std::function<void ()> > tasks_;\n    \n    static const int  kMaxThreads = 256;\n};\n\n\ntemplate <typename F, typename... Args>\nauto ThreadPool::ExecuteTask(F&& f, Args&&... args) -> std::future<typename std::result_of<F (Args...)>::type>\n{\n    using  resultType = typename std::result_of<F (Args...)>::type;\n    \n    auto task = std::make_shared<std::packaged_task<resultType ()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));\n    \n    {\n        std::unique_lock<std::mutex>     guard(mutex_);\n        if (shutdown_)\n        {\n            return std::future<resultType>();\n        }\n        \n        tasks_.emplace_back( [=]() { (*task)(); } );\n        if (waiters_ == 0)\n        {\n            _CreateWorker();\n        }\n        \n        cond_.notify_one();\n    }\n    \n    return task->get_future();\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/Timer.cc",
    "content": "#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n#include <cstring>\n#include \"Timer.h\"\n\nuint64_t Now()\n{\n    struct timeval now;\n    ::gettimeofday(&now, 0);\n    return  uint64_t(now.tv_sec * 1000UL + now.tv_usec / 1000UL);\n}\n\nstatic bool  IsLeapYear(int year)\n{\n    return  (year % 400 == 0 ||\n            (year % 4 == 0 && year % 100 != 0));\n}\n\nstatic int DaysOfMonth(int year, int month)\n{\n    const int monthDay[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};\n\n    if (2 == month && IsLeapYear(year))\n        return 29;\n\n    return  monthDay[month];\n}\n\n\n\nTime::Time() : ms_(0), valid_(false)\n{\n    tm_.tm_year = 0;\n    this->Now();\n}\n\nTime::Time(const Time& other) : ms_(other.ms_)\n{\n    tm_.tm_year = 0;\n    valid_ = false;\n}\n\n\nTime::Time(int hour, int min, int sec)\n{\n    this->Now();\n\n    //如果hour小于当前的hour，则换天了，如果当前天数是本月最后一天，则换月；如果本月是12月，则换年\n    int day   = GetDay();\n    int mon   = GetMonth();\n    int year  = GetYear();\n\n    bool  tomorrow = false;\n    if (hour < GetHour() ||\n       (hour == GetHour() && min < GetMinute()) ||\n       (hour == GetHour() && min == GetMinute() && sec < GetSecond()))\n        tomorrow = true;\n\n    if (tomorrow)\n    {\n        if (DaysOfMonth(year, mon) == day)\n        {\n            day = 1;\n\n            if (12 == mon)\n            {\n                mon = 1;\n                ++ year;\n            }\n            else\n                ++ mon;\n        }\n        else\n        {\n            ++ day;\n        }\n    }\n\n    // 构造tm\n    struct tm  stm;\n    stm.tm_sec = sec;\n    stm.tm_min = min;\n    stm.tm_hour= hour;\n    stm.tm_mday= day;\n    stm.tm_mon = mon - 1;\n    stm.tm_year = year - 1900;\n    stm.tm_yday = 0;\n    stm.tm_isdst = 0;\n\n    time_t tt = mktime(&stm);\n    ms_ = tt * 1000UL;\n    valid_ = false;\n}\n\nvoid Time::_UpdateTm()  const\n{\n    if (valid_)  \n        return;\n\n    valid_ = true; \n    const time_t now(ms_ / 1000UL); \n    ::localtime_r(&now, &tm_);\n}\n\nvoid Time::Now()\n{\n    ms_ = ::Now();\n    valid_ = false;\n}\n\n// from 2015 to 2025\nstatic const char* YEAR[] = { \"2015\", \"2016\", \"2017\", \"2018\",\n    \"2019\", \"2020\", \"2021\", \"2022\", \"2023\", \"2024\", \"2025\",\n     nullptr,\n};\n\nstd::size_t Time::FormatTime(char* buf) const\n{\n    static char NUMBER[60][2] = {\"\"};\n\n    static bool bFirst = true;\n    if (bFirst)\n    {\n        bFirst = false;\n        for (size_t i = 0; i < sizeof NUMBER / sizeof NUMBER[0]; ++ i)\n        {\n            char tmp[3]; \n            snprintf(tmp, 3, \"%02d\", static_cast<int>(i));\n            memcpy(NUMBER[i], tmp, 2);\n        }\n    }\n\n    _UpdateTm();\n    \n#if  1\n    memcpy(buf, YEAR[tm_.tm_year + 1900 - 2015], 4);\n    buf[4] = '-';\n    memcpy(buf + 5, NUMBER[tm_.tm_mon + 1], 2);\n    buf[7] = '-';\n    memcpy(buf + 8, NUMBER[tm_.tm_mday], 2);\n    buf[10] = '[';\n    memcpy(buf + 11, NUMBER[tm_.tm_hour], 2);\n    buf[13] = ':';\n    memcpy(buf + 14, NUMBER[tm_.tm_min], 2);\n    buf[16] = ':';\n    memcpy(buf + 17, NUMBER[tm_.tm_sec], 2);\n    buf[19] = '.';\n    snprintf(buf + 20, 5, \"%03d]\", static_cast<int>(ms_ % 1000));\n#else\n    \n    snprintf(buf, 25, \"%04d-%02d-%02d[%02d:%02d:%02d.%03d]\",\n             tm_.tm_year+1900, tm_.tm_mon+1, tm_.tm_mday,\n             tm_.tm_hour, tm_.tm_min, tm_.tm_sec,\n             static_cast<int>(ms_ % 1000));\n#endif\n    \n    return 24;\n}\n\nvoid Time::AddDelay(uint64_t delay)\n{\n    ms_   += delay;\n    valid_ = false;\n}\n\nTime& Time::operator= (const Time & other)\n{\n    if (this != &other)\n    {\n        ms_    = other.ms_;\n        valid_ = false;\n    }\n    return *this;\n}\n\n\n\n\nTimer::Timer(): next_(nullptr), prev_(nullptr)\n{\n}\n\nvoid  Timer::Init(uint32_t interval, int32_t count)\n{\n    interval_ = interval;\n    count_ = count;\n    triggerTime_.Now();\n    triggerTime_.AddDelay(interval);\n}\n\n\nbool Timer::OnTimer()\n{\n    if (!func_)\n        return false;\n\n    if (count_ < 0 || -- count_ >= 0)\n    {\n        triggerTime_.AddDelay(interval_);\n        func_();\n\n        return true;\n    }\n\n    return false;        \n}\n\nTimerManager::TimerManager() : count_(0)\n{\n    for (int i = 0; i < LIST1_SIZE; ++ i)\n    {\n        m_list1[i] = new Timer();\n    }\n\n    for (int i = 0; i < LIST_SIZE; ++ i)\n    {\n        m_list2[i] = new Timer();\n        m_list3[i] = new Timer();\n        m_list4[i] = new Timer();\n        m_list5[i] = new Timer();\n    }\n}\n\nTimerManager::~TimerManager()\n{\n    Timer* pTimer = nullptr;\n    for (int i = 0; i < LIST1_SIZE; ++ i)\n    {\n        while ((pTimer = m_list1[i]->next_) )\n        {\n            KillTimer(pTimer);\n            delete pTimer;\n        }\n    \n        delete m_list1[i];\n    }\n\n    for (int i = 0; i < LIST_SIZE; ++ i)\n    {\n        while ((pTimer = m_list2[i]->next_) )\n        {\n            KillTimer(pTimer);\n            delete pTimer;\n        }\n        delete m_list2[i];\n\n        while ((pTimer = m_list3[i]->next_) )\n        {\n            KillTimer(pTimer);\n            delete pTimer;\n        }\n        delete m_list3[i];\n\n        while ((pTimer = m_list4[i]->next_) )\n        {\n            KillTimer(pTimer);\n            delete pTimer;\n        }\n        delete m_list4[i];\n\n        while ((pTimer = m_list5[i]->next_) )\n        {\n            KillTimer(pTimer);\n            delete pTimer;\n        }\n        delete m_list5[i];\n    }\n    \n    for (auto t : freepool_)\n        delete t;\n}\n\nTimerManager&  TimerManager::Instance()\n{\n    static TimerManager mgr;\n    return  mgr;\n}\n\nTimer* TimerManager::CreateTimer()\n{\n    Timer* timer = nullptr;\n    if (freepool_.empty())\n    {\n        timer = new Timer();\n    }\n    else\n    {\n        timer = *(freepool_.begin());\n        freepool_.erase(freepool_.begin());\n    }\n\n    return timer;\n}\n\nbool TimerManager::UpdateTimers(const Time& now)\n{\n    if (count_ > 0 && lock_.try_lock())\n    {\n        decltype(timers_) tmp;\n        tmp.swap(timers_);\n        count_ = 0;\n        lock_.unlock();\n\n        for (auto timer : tmp)\n        {\n            AddTimer(timer);\n        }\n    }\n\n    const bool hasUpdated(m_lastCheckTime <= now);\n\n    while (m_lastCheckTime <= now)\n    {\n        int index = m_lastCheckTime & (LIST1_SIZE - 1);\n        if (index == 0 &&\n            !_Cacsade(m_list2, _Index(0)) &&\n            !_Cacsade(m_list3, _Index(1)) &&\n            !_Cacsade(m_list4, _Index(2)))\n        {\n            _Cacsade(m_list5, _Index(3));\n        }\n\n        m_lastCheckTime.AddDelay(1);\n\n        Timer* timer;\n        while ((timer = m_list1[index]->next_))\n        {\n            KillTimer(timer);\n            if (timer->OnTimer())\n                AddTimer(timer);\n            else\n                freepool_.insert(timer);\n        }\n    }        \n\n    return hasUpdated;\n}\n\n\nvoid TimerManager::AddTimer(Timer* timer)\n{\n    uint32_t diff      =  static_cast<uint32_t>(timer->triggerTime_ - m_lastCheckTime);\n    uint64_t trigTime  =  timer->triggerTime_.MilliSeconds();\n    Timer* pListHead   = nullptr;\n    \n    if ((int32_t)diff < 0)\n    {\n        pListHead = m_list1[m_lastCheckTime.MilliSeconds() & (LIST1_SIZE - 1)];\n    }\n    else if (diff < static_cast<uint32_t>(LIST1_SIZE))\n    {\n        pListHead = m_list1[trigTime & (LIST1_SIZE - 1)];\n    }\n    else if (diff < 1 << (LIST1_BITS + LIST_BITS))\n    {\n        pListHead = m_list2[(trigTime >> LIST1_BITS) & (LIST_SIZE - 1)];\n    }\n    else if (diff < 1 << (LIST1_BITS + 2 * LIST_BITS))\n    {\n        pListHead = m_list3[(trigTime >> (LIST1_BITS + LIST_BITS)) & (LIST_SIZE - 1)];\n    }\n    else if (diff < 1 << (LIST1_BITS + 3 * LIST_BITS))\n    {\n        pListHead = m_list4[(trigTime >> (LIST1_BITS + 2 * LIST_BITS)) & (LIST_SIZE - 1)];\n    }\n    else\n    {\n        pListHead = m_list5[(trigTime >> (LIST1_BITS + 3 * LIST_BITS)) & (LIST_SIZE - 1)];\n    }\n\n    assert(!pListHead->prev_);\n\n    timer->prev_ = pListHead;\n    timer->next_ = pListHead->next_;\n    if (pListHead->next_)\n        pListHead->next_->prev_ = timer;\n    pListHead->next_ = timer;\n    \n    freepool_.erase(timer);\n}\n\nvoid TimerManager::AsyncAddTimer(Timer* timer)\n{\n    std::lock_guard<std::mutex>  guard(lock_);\n\n    timers_.push_back(timer);\n    ++ count_;\n    assert (count_ == timers_.size());\n}\n\nvoid TimerManager::ScheduleAt(Timer* timer, const Time& triggerTime)\n{\n    if (!timer)\n        return;\n\n    timer->triggerTime_ = triggerTime;\n    AddTimer(timer);\n}\n\n\nvoid TimerManager::KillTimer(Timer* timer)\n{\n    if (!timer)\n        return;\n\n    if (timer->prev_)\n    {\n        timer->prev_->next_ = timer->next_;\n\n        if (timer->next_)\n        {\n            timer->next_->prev_ = timer->prev_;\n            timer->next_ = nullptr;\n        }\n\n        timer->prev_ = nullptr;\n    }\n}\n\nbool TimerManager::_Cacsade(Timer* pList[], int index)\n{\n    assert (pList);\n\n    if (index < 0 ||\n        index >= LIST_SIZE)\n        return  false;\n\n    assert (pList[index]);\n\n    if (!pList[index]->next_)\n        return false;\n\n    Timer* tmpListHead = pList[index]->next_;\n    pList[index]->next_ = nullptr;\n\n    while (tmpListHead)\n    {\n        Timer* next = tmpListHead->next_;\n\n        tmpListHead->next_ = nullptr;\n        tmpListHead->prev_ = nullptr;\n        \n        AddTimer(tmpListHead);\n        tmpListHead = next;\n    }\n\n    return true;\n}\n\n"
  },
  {
    "path": "QBase/Timer.h",
    "content": "\n#ifndef BERT_TIMER_H\n#define BERT_TIMER_H\n\n#include <vector>\n#include <set>\n#include <ctime>\n#include <sys/time.h>\n#include <stdint.h>\n#include <functional>\n#include <memory>\n#include <mutex>\n#include <atomic>\n\nuint64_t Now();\n\nclass Time\n{\npublic:\n    Time();\n    Time(const Time& other);\n    Time(int hour, int min, int sec); // TODO year month day, and Effective cpp item 18\n\n    void        Now();\n    uint64_t    MilliSeconds() const { return ms_; }\n    std::size_t FormatTime(char* buf) const;\n    void        AddDelay(uint64_t delay);\n\n    int GetYear()   const { _UpdateTm(); return tm_.tm_year + 1900;  } \n    int GetMonth()  const { _UpdateTm(); return tm_.tm_mon + 1;  } \n    int GetDay()    const { _UpdateTm(); return tm_.tm_mday; }\n    int GetHour()   const { _UpdateTm(); return tm_.tm_hour; }\n    int GetMinute() const { _UpdateTm(); return tm_.tm_min;  }\n    int GetSecond() const { _UpdateTm(); return tm_.tm_sec;  }\n\n    Time&    operator= (const Time& );\n    operator uint64_t() const { return ms_; }\n\nprivate:\n    uint64_t    ms_;      // milliseconds from 1970\n    mutable tm  tm_;\n    mutable bool valid_;\n    \n    void    _UpdateTm()  const;\n};\n\n\nclass Timer\n{\n    friend class TimerManager;\npublic:\n    void  Init(uint32_t interval, int32_t count = -1);\n    bool  OnTimer();\n    void  SetRemainCnt(int32_t remain) {  count_ = remain; }\n    bool  IsWorking() const {  return  prev_ != nullptr; }\n\n    template <typename F, typename... Args>\n    void SetCallback(F&& f, Args&&... args)\n    {\n        auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n        func_ = [temp]() { (void)temp(); };\n    }\n\nprivate:\n    Timer();\n\n    std::function<void ()> func_;\n    Timer* next_;\n    Timer* prev_;\n    Time     triggerTime_;\n    uint32_t interval_;\n    int32_t  count_;\n};\n\nclass TimerManager\n{\npublic:\n    ~TimerManager();\n\n    TimerManager(const TimerManager& ) = delete;\n    void operator= (const TimerManager& ) = delete;\n\n    static  TimerManager&   Instance();\n\n    bool    UpdateTimers(const Time& now);\n    void    ScheduleAt(Timer* pTimer, const Time& triggerTime);\n    void    AddTimer(Timer* timer);\n    void    AsyncAddTimer(Timer* timer);\n    void    KillTimer(Timer* pTimer);\n\n    Timer*  CreateTimer();\n\nprivate:\n    TimerManager();\n\n    bool _Cacsade(Timer* pList[], int index);\n    int _Index(int level);\n\n    static const int LIST1_BITS = 8;\n    static const int LIST_BITS  = 6;\n    static const int LIST1_SIZE = 1 << LIST1_BITS;\n    static const int LIST_SIZE  = 1 << LIST_BITS;\n\n    Time  m_lastCheckTime;\n\n    Timer* m_list1[LIST1_SIZE]; // 256 ms\n    Timer* m_list2[LIST_SIZE];  // 64 * 256ms = 16s\n    Timer* m_list3[LIST_SIZE];  // 64 * 64 * 256ms = 17m\n    Timer* m_list4[LIST_SIZE];  // 64 * 64 * 64 * 256ms = 18h\n    Timer* m_list5[LIST_SIZE];  // 64 * 64 * 64 * 64 * 256ms = 49 days\n\n    // timer pool\n    std::set<Timer* > freepool_;\n\n    // async add\n    std::mutex lock_;\n    std::atomic<std::size_t> count_;\n    std::vector<Timer* > timers_;\n};\n\ninline int TimerManager::_Index(int level)\n{\n    uint64_t current = m_lastCheckTime;\n    current >>= (LIST1_BITS + level * LIST_BITS);\n    return current & (LIST_SIZE - 1);\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/UnboundedBuffer.cc",
    "content": "\n#include \"UnboundedBuffer.h\"\n#include <iostream>\n#include <limits>\n#include <cassert>\n\nnamespace qedis\n{\n\nconst std::size_t UnboundedBuffer::MAX_BUFFER_SIZE = std::numeric_limits<std::size_t>::max() / 2;\n\n\nstd::size_t UnboundedBuffer::Write(const void* pData, std::size_t nSize)\n{\n    return PushData(pData, nSize);\n}\n\nstd::size_t UnboundedBuffer::PushData(const void* pData, std::size_t nSize)\n{\n    std::size_t nBytes  = PushDataAt(pData, nSize);\n    AdjustWritePtr(nBytes);\n\n    return nBytes;\n}\n\nstd::size_t UnboundedBuffer::PushDataAt(const void* pData, std::size_t nSize, std::size_t offset)\n{\n    if (!pData || nSize == 0)\n        return 0;\n\n    if (ReadableSize() == UnboundedBuffer::MAX_BUFFER_SIZE)\n        return 0;\n\n    _AssureSpace(nSize + offset);\n\n    assert (nSize + offset <= WritableSize());\n\n   \t::memcpy(&buffer_[writePos_ + offset], pData, nSize);\n    return  nSize;\n}\n\nstd::size_t UnboundedBuffer::PeekData(void* pBuf, std::size_t nSize)\n{\n    std::size_t nBytes  = PeekDataAt(pBuf, nSize);\n    AdjustReadPtr(nBytes);\n\n    return nBytes;\n}\n\nstd::size_t UnboundedBuffer::PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset)\n{\n    const std::size_t dataSize = ReadableSize();\n    if (!pBuf ||\n         nSize == 0 ||\n         dataSize <= offset)\n        return 0;\n\n    if (nSize + offset > dataSize)\n        nSize = dataSize - offset;\n\n\t::memcpy(pBuf, &buffer_[readPos_ + offset], nSize);\n\n    return nSize;\n}\n\n\nvoid UnboundedBuffer::_AssureSpace(std::size_t nSize)\n{\n    if (nSize <= WritableSize())\n        return;\n\n    std::size_t maxSize = buffer_.size();\n\n    while (nSize > WritableSize() + readPos_)\n    {\n        if (maxSize < 64)\n            maxSize = 64;\n        else if (maxSize <= UnboundedBuffer::MAX_BUFFER_SIZE)\n            maxSize += (maxSize / 2);\n        else \n            break;\n\n        buffer_.resize(maxSize);\n    }\n        \n    if (readPos_ > 0)\n    {\n        std::size_t dataSize = ReadableSize();\n        std::cout << dataSize << \" bytes moved from \" << readPos_ << std::endl;\n        ::memmove(&buffer_[0], &buffer_[readPos_], dataSize);\n        readPos_  = 0;\n        writePos_ = dataSize;\n    }\n}\n\nvoid UnboundedBuffer::Shrink(bool tight)\n{\n    assert (buffer_.capacity() == buffer_.size());\n\n    if (buffer_.empty())\n    { \n        assert (readPos_ == 0);\n        assert (writePos_ == 0);\n        return;\n    }\n\n    std::size_t oldCap   = buffer_.size();\n    std::size_t dataSize = ReadableSize();\n    if (!tight && dataSize > oldCap / 2)\n        return;\n\n    std::vector<char>  tmp;\n    tmp.resize(dataSize);\n    memcpy(&tmp[0], &buffer_[readPos_], dataSize);\n    tmp.swap(buffer_);\n\n    readPos_  = 0;\n    writePos_ = dataSize;\n\n    std::cout << oldCap << \" shrink to \" << buffer_.size() << std::endl;\n}\n\nvoid UnboundedBuffer::Clear()\n{\n    readPos_ = writePos_ = 0; \n}\n\n\nvoid UnboundedBuffer::Swap(UnboundedBuffer& buf)\n{\n    buffer_.swap(buf.buffer_);\n    std::swap(readPos_, buf.readPos_);\n    std::swap(writePos_, buf.writePos_);\n}\n\n#if 0\nint main()\n{\n    UnboundedBuffer    buf;\n    std::size_t ret = buf.PushData(\"hello\", 5);\n    assert (ret == 5);\n\n    char tmp[10];\n    ret = buf.PeekData(tmp, sizeof tmp);\n    assert(ret == 5);\n    assert(tmp[0] == 'h');\n\n    assert(buf.IsEmpty());\n\n    ret = buf.PushData(\"world\", 5);\n    assert (ret == 5);\n    ret = buf.PushData(\"abcde\", 5);\n    assert (ret == 5);\n    ret = buf.PeekData(tmp, 5);\n    assert(tmp[0] == 'w');\n\n    buf.Clear();\n    buf.Shrink();\n\n#if 1\n    ret = buf.PeekData(tmp, 5);\n    if (ret == 5)\n    {\n        assert(tmp[0] == 'a');\n        assert(tmp[1] == 'b');\n    }\n#endif\n    buf.Shrink();\n\n    return 0;\n}\n\n#endif\n\n}\n"
  },
  {
    "path": "QBase/UnboundedBuffer.h",
    "content": "\n#ifndef BERT_UNBOUNDEDBUFFER_H\n#define BERT_UNBOUNDEDBUFFER_H\n\n#include <cstring>\n#include <vector>\n\nnamespace qedis\n{\n\nclass UnboundedBuffer\n{\npublic:\n    UnboundedBuffer() :\n        readPos_(0),\n        writePos_(0)\n    {\n    }\n\n    std::size_t PushDataAt(const void* pData, std::size_t nSize, std::size_t offset = 0);\n    std::size_t PushData(const void* pData, std::size_t nSize);\n    std::size_t Write(const void* pData, std::size_t nSize);\n    void AdjustWritePtr(std::size_t nBytes) {   writePos_ += nBytes; }\n\n    std::size_t  PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset = 0);\n    std::size_t  PeekData(void* pBuf, std::size_t nSize);\n    void AdjustReadPtr(std::size_t nBytes) {   readPos_  += nBytes; }\n\n    char* ReadAddr()  {  return &buffer_[readPos_];  }\n    char* WriteAddr() {  return &buffer_[writePos_]; }\n\n    bool IsEmpty() const { return ReadableSize() == 0; }\n    std::size_t ReadableSize() const {  return writePos_ - readPos_;  }\n    std::size_t WritableSize() const {  return buffer_.size() - writePos_;  }\n\n    void Shrink(bool tight = false);\n    void Clear();\n    void Swap(UnboundedBuffer& buf);\n\n    static const std::size_t  MAX_BUFFER_SIZE;\nprivate:\n    void     _AssureSpace(std::size_t size);\n    std::size_t readPos_;\n    std::size_t writePos_;\n    std::vector<char>  buffer_;\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QBase/lzf/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": "QBase/lzf/lzfP.h",
    "content": "/*\n * Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#ifndef LZFP_h\n#define LZFP_h\n\n#define STANDALONE 1 /* at the moment, this is ok. */\n\n#ifndef STANDALONE\n# include \"lzf.h\"\n#endif\n\n/*\n * Size of hashtable is (1 << HLOG) * sizeof (char *)\n * decompression is independent of the hash table size\n * the difference between 15 and 14 is very small\n * for small blocks (and 14 is usually a bit faster).\n * For a low-memory/faster configuration, use HLOG == 13;\n * For best compression, use 15 or 16 (or more, up to 23).\n */\n#ifndef HLOG\n# define HLOG 16\n#endif\n\n/*\n * Sacrifice very little compression quality in favour of compression speed.\n * This gives almost the same compression as the default code, and is\n * (very roughly) 15% faster. This is the preferred mode of operation.\n */\n#ifndef VERY_FAST\n# define VERY_FAST 1\n#endif\n\n/*\n * Sacrifice some more compression quality in favour of compression speed.\n * (roughly 1-2% worse compression for large blocks and\n * 9-10% for small, redundant, blocks and >>20% better speed in both cases)\n * In short: when in need for speed, enable this for binary data,\n * possibly disable this for text data.\n */\n#ifndef ULTRA_FAST\n# define ULTRA_FAST 0\n#endif\n\n/*\n * Unconditionally aligning does not cost very much, so do it if unsure\n */\n#ifndef STRICT_ALIGN\n# define STRICT_ALIGN !(defined(__i386) || defined (__amd64))\n#endif\n\n/*\n * You may choose to pre-set the hash table (might be faster on some\n * modern cpus and large (>>64k) blocks, and also makes compression\n * deterministic/repeatable when the configuration otherwise is the same).\n */\n#ifndef INIT_HTAB\n# define INIT_HTAB 0\n#endif\n\n/*\n * Avoid assigning values to errno variable? for some embedding purposes\n * (linux kernel for example), this is necessary. NOTE: this breaks\n * the documentation in lzf.h.\n */\n#ifndef AVOID_ERRNO\n# define AVOID_ERRNO 0\n#endif\n\n/*\n * Whether to pass the LZF_STATE variable as argument, or allocate it\n * on the stack. For small-stack environments, define this to 1.\n * NOTE: this breaks the prototype in lzf.h.\n */\n#ifndef LZF_STATE_ARG\n# define LZF_STATE_ARG 0\n#endif\n\n/*\n * Whether to add extra checks for input validity in lzf_decompress\n * and return EINVAL if the input stream has been corrupted. This\n * only shields against overflowing the input buffer and will not\n * detect most corrupted streams.\n * This check is not normally noticeable on modern hardware\n * (<1% slowdown), but might slow down older cpus considerably.\n */\n#ifndef CHECK_INPUT\n# define CHECK_INPUT 1\n#endif\n\n/*****************************************************************************/\n/* nothing should be changed below */\n\ntypedef unsigned char u8;\n\ntypedef const u8 *LZF_STATE[1 << (HLOG)];\n\n#if !STRICT_ALIGN\n/* for unaligned accesses we need a 16 bit datatype. */\n# include <limits.h>\n# if USHRT_MAX == 65535\n    typedef unsigned short u16;\n# elif UINT_MAX == 65535\n    typedef unsigned int u16;\n# else\n#  undef STRICT_ALIGN\n#  define STRICT_ALIGN 1\n# endif\n#endif\n\n#if ULTRA_FAST\n# if defined(VERY_FAST)\n#  undef VERY_FAST\n# endif\n#endif\n\n#if INIT_HTAB\n# ifdef __cplusplus\n#  include <cstring>\n# else\n#  include <string.h>\n# endif\n#endif\n\n#endif\n\n"
  },
  {
    "path": "QBase/lzf/lzf_c.c",
    "content": "/*\n * Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#include \"lzfP.h\"\n\n#define HSIZE (1 << (HLOG))\n\n/*\n * don't play with this unless you benchmark!\n * decompression is not dependent on the hash function\n * the hashing function might seem strange, just believe me\n * it works ;)\n */\n#ifndef FRST\n# define FRST(p) (((p[0]) << 8) | p[1])\n# define NEXT(v,p) (((v) << 8) | p[2])\n# if ULTRA_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h  ) & (HSIZE - 1))\n# elif VERY_FAST\n#  define IDX(h) ((( h             >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# else\n#  define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1))\n# endif\n#endif\n/*\n * IDX works because it is very similar to a multiplicative hash, e.g.\n * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1))\n * the latter is also quite fast on newer CPUs, and compresses similarly.\n *\n * the next one is also quite good, albeit slow ;)\n * (int)(cos(h & 0xffffff) * 1e6)\n */\n\n#if 0\n/* original lzv-like hash function, much worse and thus slower */\n# define FRST(p) (p[0] << 5) ^ p[1]\n# define NEXT(v,p) ((v) << 5) ^ p[2]\n# define IDX(h) ((h) & (HSIZE - 1))\n#endif\n\n#define        MAX_LIT        (1 <<  5)\n#define        MAX_OFF        (1 << 13)\n#define        MAX_REF        ((1 << 8) + (1 << 3))\n\n#if __GNUC__ >= 3\n# define expect(expr,value)         __builtin_expect ((expr),(value))\n# define inline                     inline\n#else\n# define expect(expr,value)         (expr)\n# define inline                     static\n#endif\n\n#define expect_false(expr) expect ((expr) != 0, 0)\n#define expect_true(expr)  expect ((expr) != 0, 1)\n\n/*\n * compressed format\n *\n * 000LLLLL <L+1>    ; literal\n * LLLooooo oooooooo ; backref L\n * 111ooooo LLLLLLLL oooooooo ; backref L+7\n *\n */\n\nunsigned int\nlzf_compress (const void *const in_data, unsigned int in_len,\n\t      void *out_data, unsigned int out_len\n#if LZF_STATE_ARG\n              , LZF_STATE htab\n#endif\n              )\n{\n#if !LZF_STATE_ARG\n  LZF_STATE htab;\n#endif\n  const u8 **hslot;\n  const u8 *ip = (const u8 *)in_data;\n        u8 *op = (u8 *)out_data;\n  const u8 *in_end  = ip + in_len;\n        u8 *out_end = op + out_len;\n  const u8 *ref;\n\n  /* off requires a type wide enough to hold a general pointer difference.\n   * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only\n   * works for differences within a single object). We also assume that no\n   * no bit pattern traps. Since the only platform that is both non-POSIX\n   * and fails to support both assumptions is windows 64 bit, we make a\n   * special workaround for it.\n   */\n#if defined (WIN32) && defined (_M_X64)\n  unsigned _int64 off; /* workaround for missing POSIX compliance */\n#else\n  unsigned long off;\n#endif\n  unsigned int hval;\n  int lit;\n\n  if (!in_len || !out_len)\n    return 0;\n\n#if INIT_HTAB\n  memset (htab, 0, sizeof (htab));\n# if 0\n  for (hslot = htab; hslot < htab + HSIZE; hslot++)\n    *hslot++ = ip;\n# endif\n#endif\n\n  lit = 0; op++; /* start run */\n\n  hval = FRST (ip);\n  while (ip < in_end - 2)\n    {\n      hval = NEXT (hval, ip);\n      hslot = htab + IDX (hval);\n      ref = *hslot; *hslot = ip;\n\n      if (1\n#if INIT_HTAB\n          && ref < ip /* the next test will actually take care of this, but this is faster */\n#endif\n          && (off = ip - ref - 1) < MAX_OFF\n          && ip + 4 < in_end\n          && ref > (u8 *)in_data\n#if STRICT_ALIGN\n          && ref[0] == ip[0]\n          && ref[1] == ip[1]\n          && ref[2] == ip[2]\n#else\n          && *(u16 *)ref == *(u16 *)ip\n          && ref[2] == ip[2]\n#endif\n        )\n        {\n          /* match found at *ref++ */\n          unsigned int len = 2;\n          unsigned int maxlen = (unsigned int)(in_end - ip - len);\n          maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;\n\n          op [- lit - 1] = lit - 1; /* stop run */\n          op -= !lit; /* undo run if length is zero */\n\n          if (expect_false (op + 3 + 1 >= out_end))\n            return 0;\n\n          for (;;)\n            {\n              if (expect_true (maxlen > 16))\n                {\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                  len++; if (ref [len] != ip [len]) break;\n                }\n\n              do\n                len++;\n              while (len < maxlen && ref[len] == ip[len]);\n\n              break;\n            }\n\n          len -= 2; /* len is now #octets - 1 */\n          ip++;\n\n          if (len < 7)\n            {\n              *op++ = (off >> 8) + (len << 5);\n            }\n          else\n            {\n              *op++ = (off >> 8) + (  7 << 5);\n              *op++ = len - 7;\n            }\n\n          *op++ = off;\n          lit = 0; op++; /* start run */\n\n          ip += len + 1;\n\n          if (expect_false (ip >= in_end - 2))\n            break;\n\n#if ULTRA_FAST || VERY_FAST\n          --ip;\n# if VERY_FAST && !ULTRA_FAST\n          --ip;\n# endif\n          hval = FRST (ip);\n\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip;\n          ip++;\n\n# if VERY_FAST && !ULTRA_FAST\n          hval = NEXT (hval, ip);\n          htab[IDX (hval)] = ip;\n          ip++;\n# endif\n#else\n          ip -= len + 1;\n\n          do\n            {\n              hval = NEXT (hval, ip);\n              htab[IDX (hval)] = ip;\n              ip++;\n            }\n          while (len--);\n#endif\n        }\n      else\n        {\n          /* one more literal byte we must copy */\n          if (expect_false (op >= out_end))\n            return 0;\n\n          lit++; *op++ = *ip++;\n\n          if (expect_false (lit == MAX_LIT))\n            {\n              op [- lit - 1] = lit - 1; /* stop run */\n              lit = 0; op++; /* start run */\n            }\n        }\n    }\n\n  if (op + 3 > out_end) /* at most 3 bytes can be missing here */\n    return 0;\n\n  while (ip < in_end)\n    {\n      lit++; *op++ = *ip++;\n\n      if (expect_false (lit == MAX_LIT))\n        {\n          op [- lit - 1] = lit - 1; /* stop run */\n          lit = 0; op++; /* start run */\n        }\n    }\n\n  op [- lit - 1] = lit - 1; /* end run */\n  op -= !lit; /* undo run if length is zero */\n\n  return (unsigned int)(op - (u8 *)out_data);\n}\n\n"
  },
  {
    "path": "QBase/lzf/lzf_d.c",
    "content": "/*\n * Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>\n * \n * Redistribution and use in source and binary forms, with or without modifica-\n * tion, are permitted provided that the following conditions are met:\n * \n *   1.  Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n * \n *   2.  Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-\n * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\n * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-\n * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-\n * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * Alternatively, the contents of this file may be used under the terms of\n * the GNU General Public License (\"GPL\") version 2 or any later version,\n * in which case the provisions of the GPL are applicable instead of\n * the above. If you wish to allow the use of your version of this file\n * only under the terms of the GPL and not to allow others to use your\n * version of this file under the BSD license, indicate your decision\n * by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL. If you do not delete the\n * provisions above, a recipient may use your version of this file under\n * either the BSD or the GPL.\n */\n\n#include \"lzfP.h\"\n\n#if AVOID_ERRNO\n# define SET_ERRNO(n)\n#else\n# include <errno.h>\n# define SET_ERRNO(n) errno = (n)\n#endif\n\n/*\n#if (__i386 || __amd64) && __GNUC__ >= 3\n# define lzf_movsb(dst, src, len)                \\\n   asm (\"rep movsb\"                              \\\n        : \"=D\" (dst), \"=S\" (src), \"=c\" (len)     \\\n        :  \"0\" (dst),  \"1\" (src),  \"2\" (len));\n#endif\n*/\n\nunsigned int \nlzf_decompress (const void *const in_data,  unsigned int in_len,\n                void             *out_data, unsigned int out_len)\n{\n  u8 const *ip = (const u8 *)in_data;\n  u8       *op = (u8 *)out_data;\n  u8 const *const in_end  = ip + in_len;\n  u8       *const out_end = op + out_len;\n\n  do\n    {\n      unsigned int ctrl = *ip++;\n\n      if (ctrl < (1 << 5)) /* literal run */\n        {\n          ctrl++;\n\n          if (op + ctrl > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n#if CHECK_INPUT\n          if (ip + ctrl > in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n\n#ifdef lzf_movsb\n          lzf_movsb (op, ip, ctrl);\n#else\n          do\n            *op++ = *ip++;\n          while (--ctrl);\n#endif\n        }\n      else /* back reference */\n        {\n          unsigned int len = ctrl >> 5;\n\n          u8 *ref = op - ((ctrl & 0x1f) << 8) - 1;\n\n#if CHECK_INPUT\n          if (ip >= in_end)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n#endif\n          if (len == 7)\n            {\n              len += *ip++;\n#if CHECK_INPUT\n              if (ip >= in_end)\n                {\n                  SET_ERRNO (EINVAL);\n                  return 0;\n                }\n#endif\n            }\n\n          ref -= *ip++;\n\n          if (op + len + 2 > out_end)\n            {\n              SET_ERRNO (E2BIG);\n              return 0;\n            }\n\n          if (ref < (u8 *)out_data)\n            {\n              SET_ERRNO (EINVAL);\n              return 0;\n            }\n\n#ifdef lzf_movsb\n          len += 2;\n          lzf_movsb (op, ref, len);\n#else\n          *op++ = *ref++;\n          *op++ = *ref++;\n\n          do\n            *op++ = *ref++;\n          while (--len);\n#endif\n        }\n    }\n  while (ip < in_end);\n\n  return (unsigned int)(op - (u8 *)out_data);\n}\n\n"
  },
  {
    "path": "QSentinel/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nLINK_DIRECTORIES(../../leveldb)\n\nAUX_SOURCE_DIRECTORY(. CLUSTER_SRC)\nAUX_SOURCE_DIRECTORY(./zookeeper CLUSTER_SRC)\n\nSET(LIBRARY_OUTPUT_PATH ../../bin)\nADD_LIBRARY(qcluster SHARED ${CLUSTER_SRC})\n\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore)\n\nTARGET_LINK_LIBRARIES(qcluster; qbaselib; qediscore; leveldb; pthread)\nADD_DEPENDENCIES(qcluster qediscore qbaselib leveldb)\n\nSET_TARGET_PROPERTIES(qcluster PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "QSentinel/QClusterClient.cc",
    "content": "#if QEDIS_CLUSTER\n\n#include \"Log/Logger.h\"\n#include \"Server.h\"\n\n#include \"QConfig.h\"\n#include \"QCommand.h\"\n#include \"QClusterClient.h\"\n\n#if USE_ZOOKEEPER\n#include \"zookeeper/ZookeeperConn.h\"\n#endif\n\nnamespace qedis\n{\n\nPacketLength QClusterClient::_HandlePacket(const char* data, std::size_t bytes)\n{\n    const char* ptr = data;\n    \n    bool result = conn_->ParseMessage(ptr, bytes);\n    if (!result)\n    {\n        OnError(); \n        return 0;\n    }\n\n    return static_cast<PacketLength>(ptr - data);\n}\n\nbool QClusterClient::Init(int fd, const SocketAddr& peer)\n{ \n    if (!StreamSocket::Init(fd, peer))\n        return false;\n\n    auto me = std::static_pointer_cast<QClusterClient>(shared_from_this()); \n    SocketAddr myAddr(g_config.ip.c_str(), g_config.port);\n\n#if USE_ZOOKEEPER\n    QClusterConn* conn = new ZookeeperConn(me, g_config.setid, myAddr.ToString());\n#else\n#error \"Only support zookeeper for now, supporting etcd is in progress\"\n#endif\n\n    conn->SetOnBecomeMaster([](const std::vector<SocketAddr>& slaves) {\n        INF << \"I become master\";\n        std::vector<QString> cmd {\"slaveof\", \"no\", \"one\"};\n        slaveof(cmd, nullptr);\n\n        for (const auto& addr : slaves)\n        {\n            INF << \"Try connect to slave \" << addr.ToString();\n            // connect to slaves and send 'slave of me' \n            Server::Instance()->TCPConnect(addr, ConnectionTag::kSlaveClient);\n        }\n        // 读取所有set的数据保存其版本号 \n        // *set的数据格式是  1,3,4,7|2:1,4\n        // 含义:本set负责slot 1347，但是现在正在将slot 1,4迁移到set2上\n    });\n\n    conn->SetOnBecomeSlave([](const std::string& master) {\n        INF << \"I become slave of \" << master;\n        std::vector<QString> cmd(SplitString(master, ':'));\n        slaveof({\"slaveof\", cmd[0], cmd[1]}, nullptr);\n    });\n\n    conn_.reset(conn);\n\n    return true; \n}\n\nvoid QClusterClient::OnConnect()\n{\n    conn_->OnConnect();\n}\n\nvoid QClusterClient::OnDisconnect()\n{\n    conn_->OnDisconnect();\n}\n\n} // end namespace qedis\n\n#endif\n\n"
  },
  {
    "path": "QSentinel/QClusterClient.h",
    "content": "#ifndef BERT_QCLUSTERCLIENT_H\n#define BERT_QCLUSTERCLIENT_H\n\n#if QEDIS_CLUSTER\n\n#include \"StreamSocket.h\"\n#include \"QClusterInterface.h\"\n\nnamespace ConnectionTag\n{\n   const int kSentinelClient = 2;\n}\n\n\nnamespace qedis\n{\n\nclass QClusterClient: public StreamSocket\n{\npublic:\n    void OnConnect() override;\n    void OnDisconnect() override;\n    bool Init(int fd, const SocketAddr& peer);\n    \nprivate:\n    PacketLength _HandlePacket(const char*, std::size_t) override;\n\n    std::unique_ptr<QClusterConn> conn_;\n};\n    \n}\n\n#endif // endif QEDIS_CLUSTER\n\n#endif // endif BERT_QCLUSTERCLIENT_H\n\n"
  },
  {
    "path": "QSentinel/QClusterInterface.h",
    "content": "#ifndef BERT_CLUSTERINTERFACE_H\n#define BERT_CLUSTERINTERFACE_H\n\n#if QEDIS_CLUSTER\n\n#include <vector>\n#include <string>\n#include <functional>\n\n#include \"Socket.h\"\n\nnamespace ConnectionTag\n{\n   const int kSlaveClient = 3;\n}\n\nnamespace qedis\n{\n\nclass QClusterConn\n{ \npublic:\n    virtual ~QClusterConn()\n    {\n    }\n\n    void SetOnBecomeMaster(std::function<void (const std::vector<SocketAddr>& )> cb)\n    {\n        onBecomeMaster_ = std::move(cb);\n    }\n\n    void SetOnBecomeSlave(std::function<void (const std::string& )> cb)\n    {\n        onBecomeSlave_ = std::move(cb);\n    }\n\npublic:\n    virtual bool ParseMessage(const char*& data, size_t len) = 0;\n    virtual void OnConnect() = 0;\n    virtual void OnDisconnect() = 0;\n\nprotected:\n    std::function<void (const std::vector<SocketAddr>& )> onBecomeMaster_;\n    std::function<void (const std::string& )> onBecomeSlave_;\n\n};\n\n} // end namespace qedis\n\n#endif // endif QEDIS_CLUSTER\n\n#endif // endif BERT_CLUSTERINTERFACE_H\n\n"
  },
  {
    "path": "QSentinel/README.md",
    "content": "# Qedis高可用集群\n\n利用Zookeeper实现的Qedis高可用集群，功能类似redis-sentinel。在此基础上可实现sacle-out集群。\n\n## 环境需求\n* C++11、CMake\n* zookeeper\n* Linux 或 MAC OS\n\n## 集群特性\n 搭建Zookeeper，监视一组互为主备的Qedis进程以实现高可用;\n\n 当然也可以使用官方redis-sentinel。\n\n scale-out集群正在开发中...\n\n## 简单原理\n\n    /servers\n        /set-1\n            /qedis-(127.0.0.1:6379)-0001\n            /qedis-(127.0.0.1:6381)-0003\n            /qedis-(127.0.0.1:6382)-0004\n            /qedis-(127.0.0.1:6385)-0007\n        /set-2\n            /qedis-(127.0.0.1:16379)-0004\n            /qedis-(127.0.0.1:16389)-0007\n            /qedis-(127.0.0.1:16399)-0008\n\n 一组Qedis进程形成一个set，set内最多只有一个master，其它都是slave，且没有级联复制结构。\n\n 通过配置文件中setid来配置set，相同setid的Qedis进程将形成一个set。\n\n 通过设置配置文件中cluster开关，Qedis在启动时，将尝试向Zookeeper的/servers/set-{id}/下创建自己的临时顺序节点。\n\n 创建成功后，获取孩子列表，看自己的节点序号是不是最小。\n\n 如果是最小，则是master，向所有孩子发送slaveof my_addr的命令；\n\n 如果不是，则监视比自己序号大的孩子中序号最小的节点。比如我的序号是7，孩子列表序号是1，3，4，7，则我监视4节点。\n\n 当我收到监视的节点被删除的通知，则判断自己是否是master（因为启动时已经获得孩子列表了）。\n\n 是或不是，都继续重复上述过的逻辑。\n"
  },
  {
    "path": "QSentinel/zookeeper/ZookeeperConn.cc",
    "content": "#if QEDIS_CLUSTER\n\n#include <unistd.h>\n#include \"ZookeeperConn.h\"\n#include \"Log/Logger.h\"\n#include \"QCommon.h\"\n\n#include \"zookeeper.jute.h\"\n#include \"proto.h\"\n\nstatic int deserialize_prime_response(struct prime_struct* req, AttachedBuffer& buffer)\n{\n    buffer >> req->len;\n    req->len = ntohl(req->len); \n\n    buffer >> req->protocolVersion;\n    req->protocolVersion = ntohl(req->protocolVersion); \n\n    buffer >> req->timeOut;\n    req->timeOut = ntohl(req->timeOut);\n\n    buffer >> req->sessionId;\n    req->sessionId = htonll(req->sessionId); \n\n    buffer >> req->passwd_len;\n    req->passwd_len = ntohl(req->passwd_len); \n\n    memcpy(req->passwd, buffer.ReadAddr(), sizeof(req->passwd)); \n    return 0;\n}\n          \n          \nconst int kTimeout = 15 * 1000; // ms\n\nnamespace qedis\n{\n    \n    \nZookeeperConn::ZookeeperConn(const std::shared_ptr<StreamSocket>& c, int setId, const std::string& addr) :\n    xid_(0),\n    setId_(setId),\n    addr_(addr),\n    state_(State::kNone),\n    pingTimer_(nullptr),\n    sock_(c)\n{\n    lastPing_.tv_sec = 0;\n    lastPing_.tv_usec = 0;\n    sessionInfo_.passwd[0] = '\\0';\n    sessionFile_ = \"zk.session\" + addr;\n}\n\nZookeeperConn::~ZookeeperConn()\n{\n}\n\nbool ZookeeperConn::ParseMessage(const char*& data, size_t len)\n{\n    switch (state_)\n    {\n    case State::kHandshaking:\n        {\n            if (len < HANDSHAKE_RSP_SIZE)\n                return true;\n\n            struct prime_struct rsp;\n            AttachedBuffer buffer(const_cast<char* >(data), len);\n            deserialize_prime_response(&rsp, buffer);\n            data += sizeof(int) + rsp.len;\n        \n            if (!_ProcessHandshake(rsp))\n                return false;\n        }\n        break;\n\n    case State::kConnected:\n        {\n            if (len < 4)\n                return true;\n\n            int thisLen = *(int*)data;\n            thisLen = ntohl(thisLen);\n            if (sizeof(thisLen) + thisLen > len)\n                return true;\n\n            struct ReplyHeader hdr;\n            struct iarchive *ia = create_buffer_iarchive(const_cast<char* >(data) + 4, thisLen); \n            deserialize_ReplyHeader(ia, \"hdr\", &hdr);\n\n            // update zxid\n            if (hdr.zxid > 0)\n                sessionInfo_.lastSeenZxid = hdr.zxid;\n\n            if (!_ProcessResponse(hdr, ia))\n                return false;\n\n            data += thisLen + sizeof(thisLen);\n        }\n        break;\n\n    default:\n        assert (false);\n        break;\n    }\n\n    return true;\n}\n\nvoid ZookeeperConn::OnConnect()\n{\n    assert (state_ == State::kNone);\n\n    {\n        auto del = [this](FILE* fp) {\n            ::fclose(fp);\n            ::unlink(this->sessionFile_.c_str());\n        };\n\n        std::unique_ptr<FILE, decltype(del)> _(fopen(sessionFile_.data(), \"rb\"), del);\n        FILE* const fp = _.get();\n\n        if (fp)\n            fread(&sessionInfo_, sizeof sessionInfo_, 1, fp);\n    }\n\n    char buffer_req[HANDSHAKE_REQ_SIZE];\n    int len = sizeof(buffer_req);\n    \n    struct connect_req req; \n    req.protocolVersion = 0; \n    req.sessionId = sessionInfo_.sessionId;\n    req.passwd_len = sizeof(req.passwd);\n    req.timeOut = kTimeout;\n    req.lastZxidSeen = sessionInfo_.lastSeenZxid;\n    memcpy(req.passwd, sessionInfo_.passwd, req.passwd_len);\n                    \n    StackBuffer<HANDSHAKE_REQ_SIZE + 4> buf;\n    buf << htonl(len)\n        << htonl(req.protocolVersion)\n        << htonll(req.lastZxidSeen)\n        << htonl(req.timeOut)\n        << htonll(req.sessionId)\n        << htonl(req.passwd_len);\n\n    if (req.passwd_len > 0)\n        buf.PushData(req.passwd, req.passwd_len);\n\n    auto s = sock_.lock();\n    if (s)\n    {\n        state_ = State::kHandshaking;\n        s->SendPacket(buf);\n    }\n}\n\nvoid ZookeeperConn::OnDisconnect()\n{\n    if (pingTimer_)\n        TimerManager::Instance().KillTimer(pingTimer_);\n}\n\nstatic struct ACL _OPEN_ACL_UNSAFE_ACL[] = {{0x1f,\n                                                 {const_cast<char*>(\"world\"),\n                                                  const_cast<char*>(\"anyone\")\n                                                 }\n                                           }};\nstruct ACL_vector ZOO_OPEN_ACL_UNSAFE = { 1, _OPEN_ACL_UNSAFE_ACL};\n\n    \nstatic std::string MakeParentNode(int setid)\n{\n    std::string path(\"/servers/set-\");\n    path += std::to_string(setid); \n\n    return path;\n}\n\n// /servers/set-{setid}/qedis(ip:port)-xxxseq\nstatic std::string MakeNodePath(int setid, const std::string& addr)\n{\n    std::string path(MakeParentNode(setid));\n    path += \"/qedis(\" + addr + \")-\";\n\n    return path;\n}\n\nstatic int GetNodeSeq(const std::string& path)\n{\n    // /servers/set-{setid}/qedis(ip:port)-xxxseq\n    auto pos = path.find_last_of('-');\n    if (pos == std::string::npos)\n        return -1;\n\n    std::string number(path.substr(pos + 1));\n    return std::stoi(number);\n}\n\nstatic std::string GetNodeAddr(const std::string& path)\n{\n    // /servers/set-{setid}/qedis(ip:port)-xxxseq\n    auto start = path.find_first_of('(');\n    auto end = path.find_first_of(')');\n    if (start == std::string::npos ||\n        end == std::string::npos)\n        return std::string();\n\n    return path.substr(start + 1, end - start - 1);\n}\n\nvoid ZookeeperConn::_RunForMaster(int setid, const std::string& value)\n{\n    INF << __FUNCTION__ << \", setid \" << setid << \", value \" << value;\n\n    struct oarchive* oa = create_buffer_oarchive();\n    struct RequestHeader h = { STRUCT_INITIALIZER (xid , _GetXid()), STRUCT_INITIALIZER (type ,ZOO_CREATE_OP) };\n    int rc = serialize_RequestHeader(oa, \"header\", &h);\n\n    if (rc < 0) return;\n\n    std::string path(MakeNodePath(setid, addr_));\n\n    struct CreateRequest req;\n    req.path = const_cast<char* >(path.data());\n    req.data.buff = const_cast<char* >(value.data());\n    req.data.len = static_cast<int32_t>(value.size());\n    req.flags = ZOO_SEQUENCE | ZOO_EPHEMERAL;\n    req.acl = ZOO_OPEN_ACL_UNSAFE;\n    rc = rc < 0 ? rc : serialize_CreateRequest(oa, \"req\", &req);\n\n    _SendPacket(h, oa);\n}\n\nint ZookeeperConn::_GetXid() const\n{\n    return ++ xid_; \n}\n    \nbool ZookeeperConn::_ProcessHandshake(const prime_struct& rsp)\n{\n    if (sessionInfo_.sessionId && sessionInfo_.sessionId != rsp.sessionId)\n    {\n        DBG << \"expired, new session \" << rsp.sessionId;\n        return false;\n    }\n\n    const bool resumedSession = (sessionInfo_.sessionId == rsp.sessionId);\n    if (resumedSession)\n        DBG << \"resume session Id \" << rsp.sessionId;\n    else\n        DBG << \"new session Id \" << rsp.sessionId;\n\n    sessionInfo_.sessionId = rsp.sessionId;\n    memcpy(sessionInfo_.passwd, rsp.passwd, rsp.passwd_len);\n\n    std::unique_ptr<FILE, decltype(fclose)*> _(fopen(sessionFile_.data(), \"wb\"), fclose);\n    FILE* fp = _.get();\n    fwrite(&sessionInfo_, sizeof sessionInfo_, 1, fp);\n\n    state_ = State::kConnected;\n\n    _InitPingTimer();\n                \n    if (resumedSession)\n    {\n        // My node must exists\n        if (!_GetSiblings(MakeParentNode(setId_)))\n            return false;\n    }\n    else\n    {\n        // Create my node\n        _RunForMaster(setId_, addr_);\n    }\n\n    return true;\n}\n\nvoid ZookeeperConn::_InitPingTimer()\n{\n    assert (!pingTimer_);\n    pingTimer_ = TimerManager::Instance().CreateTimer();\n    pingTimer_->Init(kTimeout / 2);\n    pingTimer_->SetCallback([this]() {\n        struct oarchive *oa = create_buffer_oarchive();\n        struct RequestHeader h = { STRUCT_INITIALIZER(xid, PING_XID), STRUCT_INITIALIZER (type , ZOO_PING_OP) };\n\n        serialize_RequestHeader(oa, \"header\", &h); \n        gettimeofday(&this->lastPing_, nullptr);\n\n        _SendPacket(h, oa);\n    });\n\n    TimerManager::Instance().AsyncAddTimer(pingTimer_);\n}\n\nbool ZookeeperConn::_IsMaster() const\n{\n    if (siblings_.empty())\n        return false;\n\n    auto me = siblings_.find(seq_);\n    assert (me != siblings_.end());\n\n    return me == siblings_.begin();\n}\n\nbool ZookeeperConn::_ProcessWatchEvent(const ReplyHeader& hdr, iarchive* ia)\n{\n    struct WatcherEvent evt;\n    deserialize_WatcherEvent(ia, \"event\", &evt);\n\n    INF << \"WatcherEventType \" << evt.type\n        << \", state \" << evt.state\n        << \", path \" << evt.path;\n\n    switch (evt.type)\n    {\n        case DELETED_EVENT_DEF:\n            _OnNodeDelete(evt.path);\n            break;\n\n        default:\n            break;\n    }\n\n    deallocate_WatcherEvent(&evt);\n    return true;\n}\n\nbool ZookeeperConn::_ProcessResponse(const ReplyHeader& hdr, iarchive* ia)\n{\n    if (hdr.xid == WATCHER_EVENT_XID)\n    {\n        return _ProcessWatchEvent(hdr, ia);\n    }\n\n    // TODO process some other watcher events\n\n    if (pendingRequests_.empty())\n    {\n        ERR << \"Can not find request \" << hdr.xid;\n        return false;\n    }\n\n    const Request& req = pendingRequests_.front();\n    QEDIS_DEFER\n    {\n        pendingRequests_.pop_front();\n    };\n\n    if (req.xid != hdr.xid)\n    {\n        ERR << \"wrong req xid \" << req.xid << \", wrong order response \" << hdr.xid;\n        return false;\n    }\n\n    if (req.type != ZOO_PING_OP)\n        INF << \"req.type \" << req.type;\n\n    switch (req.type)\n    {\n    case ZOO_PING_OP:\n        {\n            timeval now;\n            gettimeofday(&now, nullptr);\n            int microseconds = (now.tv_sec - lastPing_.tv_sec) * 1000000;\n            microseconds += (now.tv_usec - lastPing_.tv_usec);\n            if (microseconds > 10 * 1000)\n                WRN << \"recv ping used microseconds \" << microseconds;\n        }\n        break;\n\n    case ZOO_CREATE_OP:\n        {\n            CreateResponse rsp;\n            if (deserialize_CreateResponse(ia, \"rsp\", &rsp) != 0)\n            {\n                ERR << \"deserialize_CreateResponse failed\";\n                return false;\n            }\n\n            QEDIS_DEFER\n            {\n                deallocate_CreateResponse(&rsp);\n            };\n\n            assert (node_.empty());\n            node_ = rsp.path;\n            seq_ = GetNodeSeq(node_);\n            assert (seq_ >= 0);\n\n            DBG << \"my node seq \" << seq_\n                << \" for my node \" << node_\n                << \", addr \" << GetNodeAddr(node_);\n\n            if (!_GetSiblings(MakeParentNode(setId_)))\n                return false;\n        }\n        break;\n\n    case ZOO_GETCHILDREN2_OP:\n        {\n            GetChildren2Response rsp;\n            if (deserialize_GetChildren2Response(ia, \"rsp\", &rsp) != 0)\n            {\n                ERR << \"deserialize_GetChildren2Response failed\";\n                return false;\n            }\n\n            QEDIS_DEFER\n            {\n                deallocate_GetChildren2Response(&rsp);\n            };\n\n            siblings_.clear();\n            for (int i = 0; i < rsp.children.count; ++ i)\n            {\n                const std::string& node = rsp.children.data[i];\n                int seq = GetNodeSeq(node);\n                assert (seq >= 0);\n\n                if (node_.empty())\n                {\n                    std::string addr = GetNodeAddr(node);\n                    if (addr == addr_)\n                    {\n                        node_ = node;\n                        seq_ = seq;\n                        DBG << \"Resumed session: my seq \" << seq_\n                            << \" for my node \" << node_\n                            << \", addr \" << GetNodeAddr(node_);\n                    }\n                }\n\n\n                INF << \"Get sibling \" << node;\n                siblings_.insert({seq, node});\n            }\n\n            auto me = siblings_.find(seq_);\n            assert (me != siblings_.end());\n            if (me == siblings_.begin())\n            {\n                // I am master!先获取节点分片信息，再执行slaveof no one等\n                if (!_GetData(MakeParentNode(setId_), true))\n                {\n                    ERR << \"_GetData failed for set \" << setId_;\n                    return false;\n                }\n            }\n            else\n            {\n                // monitor the node bigger than me\n                auto brother = -- me; // I'll watch you\n                _Exists(MakeParentNode(setId_) + \"/\" + brother->second, true);\n            }\n        }\n        break;\n\n    case ZOO_GETDATA_OP:\n        {\n            GetDataResponse drsp;\n            if (deserialize_GetDataResponse(ia, \"rsp\", &drsp) != 0)\n            {\n                ERR << \"deserialize_GetDataResponse failed\";\n                return false;\n            }\n\n            QEDIS_DEFER\n            {\n                deallocate_GetDataResponse(&drsp);\n            };\n                \n            // 获取了节点分片信息\n\n            std::vector<SocketAddr> slaves;\n            slaves.reserve(siblings_.size());\n\n            auto me = siblings_.find(seq_);\n            assert (me != siblings_.end());\n            auto slave = me;\n            for (++ slave; slave != siblings_.end(); ++ slave)\n            {\n                SocketAddr addr(GetNodeAddr(slave->second));\n                slaves.push_back(addr);\n            }\n\n#if 0\n            std::vector<int> shardings;\n            std::unordered_map<int, std::vector<int> > migration; // dst set & shardings\n            {\n                // *set的数据格式是  1,3,4,7|2:1,4\n                std::string data(drsp.data.buff, drsp.data.len);\n                std::vector<QString> tmp(SplitString(data, '|'));\n                std::vector<QString> shardingStr(SplitString(tmp[0], ','));\n                auto it = tmp.begin();\n                for (++ it; it != tmp.end(); ++ it)\n                {\n                    std::vector<QString> dstAndShardingStr(SplitString(*it, ':'));\n                    assert (dstAndShardingStr.size() == 2);\n\n                    int dstSetId = std::stoi(dstAndShardingStr[0]);\n                    std::vector<QString> migrateStr(SplitString(dstAndShardingStr[1], ','));\n                    std::vector<int> migrates;\n                    migrates.reserve(migrateStr.size());\n                    for (const auto& s : migrateStr)\n                        migrates.push_back(std::stoi(s));\n\n                    migration[dstSetId] = migrates;\n                }\n            }\n#endif\n\n            if (onBecomeMaster_)\n                onBecomeMaster_(slaves);\n        }\n        break;\n\n    case ZOO_EXISTS_OP:\n        {\n            if (hdr.err == ZNONODE)\n            {\n                _OnNodeDelete(req.path);\n            }\n            else\n            {\n                assert (hdr.err == ZOK);\n                ExistsResponse rsp;\n                if (deserialize_ExistsResponse(ia, \"rsp\", &rsp) != 0)\n                {\n                    ERR << \"deserialize_ExistsResponse failed\";\n                    return false;\n                }\n\n                QEDIS_DEFER\n                {\n                    deallocate_ExistsResponse(&rsp);\n                };\n\n                DBG << \"Exists response version \" << rsp.stat.version;\n                if (onBecomeSlave_)\n                {\n                    std::string master = GetNodeAddr(siblings_.begin()->second);\n                    if (master.empty())\n                        return false;\n\n                    onBecomeSlave_(master);\n                }\n            }\n        }\n        break;\n\n    default:\n        break;\n    }\n\n    return true;\n}\n\nbool ZookeeperConn::_GetSiblings(const std::string& parent)\n{\n    struct oarchive* oa = create_buffer_oarchive();\n\n    struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER (type ,ZOO_GETCHILDREN2_OP)};\n    struct GetChildren2Request req;\n    req.path = const_cast<char* >(parent.data());\n    req.watch = 0;\n    \n    int rc = serialize_RequestHeader(oa, \"header\", &h); \n    rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, \"req\", &req);\n\n    if (!_SendPacket(h, oa, &parent))\n        return false;\n\n    return rc >= 0;\n}\n\nbool ZookeeperConn::_GetData(const std::string& node, bool watch)\n{\n    struct oarchive* oa = create_buffer_oarchive();\n\n    struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER(type ,ZOO_GETDATA_OP)};\n    struct GetDataRequest req;\n    req.path = const_cast<char* >(node.data());\n    req.watch = watch ? 1 : 0;\n    \n    int rc = serialize_RequestHeader(oa, \"header\", &h); \n    rc = rc < 0 ? rc : serialize_GetDataRequest(oa, \"req\", &req);\n\n    if (!_SendPacket(h, oa, &node))\n        return false;\n\n    return rc >= 0;\n}\n\n\nbool ZookeeperConn::_Exists(const std::string& sibling, bool watch)\n{\n    struct oarchive* oa = create_buffer_oarchive();\n\n    struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER (type ,ZOO_EXISTS_OP)};\n    struct ExistsRequest req;\n    req.path = const_cast<char* >(sibling.data());\n    req.watch = watch ? 1 : 0;\n    \n    int rc = serialize_RequestHeader(oa, \"header\", &h); \n    rc = rc < 0 ? rc : serialize_ExistsRequest(oa, \"req\", &req);\n\n    if (!_SendPacket(h, oa, &sibling))\n        return false;\n\n    return rc >= 0;\n}\n\nbool ZookeeperConn::_SendPacket(const RequestHeader& h, struct oarchive* oa, const std::string* v)\n{\n    auto s = sock_.lock();\n    if (!s)\n        return false;\n        \n    int totalLen = htonl(get_buffer_len(oa));\n    s->SendPacket(&totalLen, sizeof totalLen);\n    s->SendPacket(get_buffer(oa), get_buffer_len(oa));\n\n    Request r;\n    r.xid = h.xid;\n    r.type = h.type;\n    if (v) r.path = *v;\n\n    pendingRequests_.emplace_back(std::move(r));\n   \n    close_buffer_oarchive(&oa, 1);\n    return true;\n}\n\nvoid ZookeeperConn::_OnNodeDelete(const std::string& node)\n{\n    const std::string siblingName = node.substr(node.find_last_of('/') + 1);\n    const int seq = GetNodeSeq(siblingName);\n    siblings_.erase(seq);\n    if (_IsMaster())\n    {\n        // Though I'll be master, I must broadcast this fact to all children.\n        bool succ = _GetSiblings(MakeParentNode(setId_));\n        if (!succ)\n            ERR << __FUNCTION__ << \", _GetSiblings failed with \" << setId_;\n    }\n    else\n    {\n        auto me = siblings_.find(seq_);\n        assert (me != siblings_.begin());\n        assert (me != siblings_.end());\n\n        auto brother = -- me; // I'll watch you\n        _Exists(MakeParentNode(setId_) + \"/\" + brother->second, true);\n    }\n}\n\n} // end namespace qedis\n\n#endif\n"
  },
  {
    "path": "QSentinel/zookeeper/ZookeeperConn.h",
    "content": "#ifndef BERT_ZOOKEEPERCONN_H\n#define BERT_ZOOKEEPERCONN_H\n\n#include <list>\n#include <map>\n#include \"StreamSocket.h\"\n#include \"../QClusterInterface.h\"\n\nstruct prime_struct;\nstruct RequestHeader;\nstruct ReplyHeader;\nstruct iarchive;\nstruct oarchive;\n\nclass Timer;\n\nnamespace qedis\n{\n\nclass ZookeeperConn : public QClusterConn\n{\npublic:\n    ZookeeperConn(const std::shared_ptr<StreamSocket>& c, int setId, const std::string& addr);\n    ~ZookeeperConn();\n\n    bool ParseMessage(const char*& data, size_t len) override;\n    void OnConnect() override;\n    void OnDisconnect() override;\n\nprivate:\n    void _RunForMaster(int setid, const std::string& val);\n    bool _ProcessHandshake(const prime_struct& rsp);\n    bool _ProcessResponse(const ReplyHeader& header, iarchive* ia);\n    bool _ProcessWatchEvent(const ReplyHeader& header, iarchive* ia);\n    bool _GetSiblings(const std::string& parent);\n    bool _GetData(const std::string& node, bool watch = true);\n    bool _Exists(const std::string& sibling, bool watch = true);\n    void _InitPingTimer();\n    bool _IsMaster() const;\n    bool _SendPacket(const RequestHeader& h, struct oarchive* oa, const std::string* = nullptr);\n    void _OnNodeDelete(const std::string& node);\n\n    int _GetXid() const;\n    mutable int xid_;\n\n    const int setId_;\n    const std::string addr_;\n\n    enum class State\n    {\n        kNone,\n        kHandshaking,\n        kConnected,\n    } state_;\n\n    struct Request\n    {\n        int type;\n        int xid;\n        std::string path;\n    };\n    std::list<Request> pendingRequests_;\n\n    timeval lastPing_;\n\n    // my node & seq\n    std::string node_;\n    int seq_;\n    // nodes in my set\n    std::map<int, std::string> siblings_;\n\n#pragma pack(1)\n    struct SessionInfo\n    {\n        int64_t sessionId{0};\n        char passwd[16];\n        // ? zk_cli doesn't persist this field\n        int64_t lastSeenZxid {0};\n    } sessionInfo_;\n#pragma pack()\n\n    std::string sessionFile_;\n    Timer* pingTimer_;\n    std::weak_ptr<StreamSocket> sock_;\n};\n\n} // end namespace qedis\n\n#endif //endif BERT_ZOOKEEPERCONN_H\n\n"
  },
  {
    "path": "QSentinel/zookeeper/proto.h",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#ifndef PROTO_H_\n#define PROTO_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\nconst int ZOO_EPHEMERAL = 1 << 0;\nconst int ZOO_SEQUENCE = 1 << 1;\n\n#define ZOO_NOTIFY_OP 0\n#define ZOO_CREATE_OP 1\n#define ZOO_DELETE_OP 2\n#define ZOO_EXISTS_OP 3\n#define ZOO_GETDATA_OP 4\n#define ZOO_SETDATA_OP 5\n#define ZOO_GETACL_OP 6\n#define ZOO_SETACL_OP 7\n#define ZOO_GETCHILDREN_OP 8\n#define ZOO_SYNC_OP 9\n#define ZOO_PING_OP 11\n#define ZOO_GETCHILDREN2_OP 12\n#define ZOO_CHECK_OP 13\n#define ZOO_MULTI_OP 14\n#define ZOO_CLOSE_OP -11\n#define ZOO_SETAUTH_OP 100\n#define ZOO_SETWATCHES_OP 101\n\n\n/* the size of connect request */\n#define HANDSHAKE_REQ_SIZE 44\n/* connect request */\nstruct connect_req {\n    int32_t protocolVersion;\n    int64_t lastZxidSeen;\n    int32_t timeOut;\n    int64_t sessionId;\n    int32_t passwd_len;\n    char passwd[16];\n};\n\n#define HANDSHAKE_RSP_SIZE 40\n/* the connect response */\nstruct prime_struct {\n    int32_t len;\n    int32_t protocolVersion;\n    int32_t timeOut;\n    int64_t sessionId;\n    int32_t passwd_len;\n    char passwd[16];\n}; \n\nenum ZOO_ERRORS {\n  ZOK = 0, /*!< Everything is OK */\n\n  /** System and server-side errors.\n   * This is never thrown by the server, it shouldn't be used other than\n   * to indicate a range. Specifically error codes greater than this\n   * value, but lesser than {@link #ZAPIERROR}, are system errors. */\n  ZSYSTEMERROR = -1,\n  ZRUNTIMEINCONSISTENCY = -2, /*!< A runtime inconsistency was found */\n  ZDATAINCONSISTENCY = -3, /*!< A data inconsistency was found */\n  ZCONNECTIONLOSS = -4, /*!< Connection to the server has been lost */\n  ZMARSHALLINGERROR = -5, /*!< Error while marshalling or unmarshalling data */\n  ZUNIMPLEMENTED = -6, /*!< Operation is unimplemented */\n  ZOPERATIONTIMEOUT = -7, /*!< Operation timeout */\n  ZBADARGUMENTS = -8, /*!< Invalid arguments */\n  ZINVALIDSTATE = -9, /*!< Invliad zhandle state */\n\n  /** API errors.\n   * This is never thrown by the server, it shouldn't be used other than\n   * to indicate a range. Specifically error codes greater than this\n   * value are API errors (while values less than this indicate a \n   * {@link #ZSYSTEMERROR}).\n   */\n  ZAPIERROR = -100,\n  ZNONODE = -101, /*!< Node does not exist */\n  ZNOAUTH = -102, /*!< Not authenticated */\n  ZBADVERSION = -103, /*!< Version conflict */\n  ZNOCHILDRENFOREPHEMERALS = -108, /*!< Ephemeral nodes may not have children */\n  ZNODEEXISTS = -110, /*!< The node already exists */\n  ZNOTEMPTY = -111, /*!< The node has children */\n  ZSESSIONEXPIRED = -112, /*!< The session has been expired by the server */\n  ZINVALIDCALLBACK = -113, /*!< Invalid callback specified */\n  ZINVALIDACL = -114, /*!< Invalid ACL specified */\n  ZAUTHFAILED = -115, /*!< Client authentication failed */\n  ZCLOSING = -116, /*!< ZooKeeper is closing */\n  ZNOTHING = -117, /*!< (not error) no server responses to process */\n  ZSESSIONMOVED = -118 /*!<session moved to another server, so operation is ignored */ \n};\n\nstatic const int WATCHER_EVENT_XID = -1;\nstatic const int PING_XID = -2;\nstatic const int AUTH_XID = -4;\nstatic const int SET_WATCHES_XID = -8;\n\n/* zookeeper event type constants */\n#define CREATED_EVENT_DEF 1\n#define DELETED_EVENT_DEF 2\n#define CHANGED_EVENT_DEF 3\n#define CHILD_EVENT_DEF 4\n#define SESSION_EVENT_DEF -1\n#define NOTWATCHING_EVENT_DEF -2\n\n\n/* zookeeper state constants */\n#define EXPIRED_SESSION_STATE_DEF -112\n#define AUTH_FAILED_STATE_DEF -113\n#define CONNECTING_STATE_DEF 1\n#define ASSOCIATING_STATE_DEF 2\n#define CONNECTED_STATE_DEF 3\n#define NOTCONNECTED_STATE_DEF 999\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*PROTO_H_*/\n"
  },
  {
    "path": "QSentinel/zookeeper/recordio.c",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#include \"recordio.h\"\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <netinet/in.h>\n\nvoid deallocate_String(char **s)\n{\n    if (*s)\n        free(*s);\n    *s = 0;\n}\n\nvoid deallocate_Buffer(struct buffer *b)\n{\n    if (b->buff)\n        free(b->buff);\n    b->buff = 0;\n}\n\nstruct buff_struct {\n    int32_t len;\n    int32_t off;\n    char *buffer;\n};\n\nstatic int resize_buffer(struct buff_struct *s, int newlen)\n{\n    char *buffer= NULL;\n    while (s->len < newlen) {\n        s->len *= 2;\n    }\n    buffer = (char*)realloc(s->buffer, s->len);\n    if (!buffer) {\n        s->buffer = 0;\n        return -ENOMEM;\n    }\n    s->buffer = buffer;\n    return 0;\n}\n\nint oa_start_record(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_end_record(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d)\n{\n    struct buff_struct *priv = oa->priv;\n    int32_t i = htonl(*d);\n    if ((priv->len - priv->off) < sizeof(i)) {\n        int rc = resize_buffer(priv, priv->len + sizeof(i));\n        if (rc < 0) return rc;\n    }\n    memcpy(priv->buffer+priv->off, &i, sizeof(i));\n    priv->off+=sizeof(i);\n    return 0;\n}\n#ifndef __APPLE__\nint64_t htonll(int64_t v)\n{\n    int i = 0;\n    char *s = (char *)&v;\n    if (htonl(1) == 1) {\n        return v;\n    }\n    for (i = 0; i < 4; i++) {\n        int tmp = s[i];\n        s[i] = s[8-i-1];\n        s[8-i-1] = tmp;\n    }\n\n    return v;\n}\n#endif\n\nint oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d)\n{\n    const int64_t i = htonll(*d);\n    struct buff_struct *priv = oa->priv;\n    if ((priv->len - priv->off) < sizeof(i)) {\n        int rc = resize_buffer(priv, priv->len + sizeof(i));\n        if (rc < 0) return rc;\n    }\n    memcpy(priv->buffer+priv->off, &i, sizeof(i));\n    priv->off+=sizeof(i);\n    return 0;\n}\nint oa_start_vector(struct oarchive *oa, const char *tag, const int32_t *count)\n{\n    return oa_serialize_int(oa, tag, count);\n}\nint oa_end_vector(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_serialize_bool(struct oarchive *oa, const char *name, const int32_t *i)\n{\n    //return oa_serialize_int(oa, name, i);\n    struct buff_struct *priv = oa->priv;\n    if ((priv->len - priv->off) < 1) {\n        int rc = resize_buffer(priv, priv->len + 1);\n        if (rc < 0)\n            return rc;\n    }\n    priv->buffer[priv->off] = (*i == 0 ? '\\0' : '\\1');\n    priv->off++;\n    return 0;\n}\nstatic const int32_t negone = -1;\nint oa_serialize_buffer(struct oarchive *oa, const char *name,\n        const struct buffer *b)\n{\n    struct buff_struct *priv = oa->priv;\n    int rc;\n    if (!b) {\n        return oa_serialize_int(oa, \"len\", &negone);\n    }\n    rc = oa_serialize_int(oa, \"len\", &b->len);\n    if (rc < 0)\n        return rc;\n    // this means a buffer of NUll \n    // with size of -1. This is \n    // waht we use in java serialization for NULL\n    if (b->len == -1) {\n      return rc;\n    }\n    if ((priv->len - priv->off) < b->len) {\n        rc = resize_buffer(priv, priv->len + b->len);\n        if (rc < 0)\n            return rc;\n    }\n    memcpy(priv->buffer+priv->off, b->buff, b->len);\n    priv->off += b->len;\n    return 0;\n}\nint oa_serialize_string(struct oarchive *oa, const char *name, char **s)\n{\n    struct buff_struct *priv = oa->priv;\n    int32_t len;\n    int rc;\n    if (!*s) {\n        oa_serialize_int(oa, \"len\", &negone);\n        return 0;\n    }\n    len = strlen(*s);\n    rc = oa_serialize_int(oa, \"len\", &len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < len) {\n        rc = resize_buffer(priv, priv->len + len);\n        if (rc < 0)\n            return rc;\n    }\n    memcpy(priv->buffer+priv->off, *s, len);\n    priv->off += len;\n    return 0;\n}\nint ia_start_record(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_end_record(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_deserialize_int(struct iarchive *ia, const char *tag, int32_t *count)\n{\n    struct buff_struct *priv = ia->priv;\n    if ((priv->len - priv->off) < sizeof(*count)) {\n        return -E2BIG;\n    }\n    memcpy(count, priv->buffer+priv->off, sizeof(*count));\n    priv->off+=sizeof(*count);\n    *count = ntohl(*count);\n    return 0;\n}\n\nint ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count)\n{\n    struct buff_struct *priv = ia->priv;\n    int64_t v = 0;\n    if ((priv->len - priv->off) < sizeof(*count)) {\n        return -E2BIG;\n    }\n    memcpy(count, priv->buffer+priv->off, sizeof(*count));\n    priv->off+=sizeof(*count);\n    v = htonll(*count); // htonll and  ntohll do the same\n    *count = v;\n    return 0;\n}\nint ia_start_vector(struct iarchive *ia, const char *tag, int32_t *count)\n{\n    return ia_deserialize_int(ia, tag, count);\n}\nint ia_end_vector(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_deserialize_bool(struct iarchive *ia, const char *name, int32_t *v)\n{\n    struct buff_struct *priv = ia->priv;\n    //fprintf(stderr, \"Deserializing bool %d\\n\", priv->off);\n    //return ia_deserialize_int(ia, name, v);\n    if ((priv->len - priv->off) < 1) {\n        return -E2BIG;\n    }\n    *v = priv->buffer[priv->off];\n    priv->off+=1;\n    //fprintf(stderr, \"Deserializing bool end %d\\n\", priv->off);\n    return 0;\n}\nint ia_deserialize_buffer(struct iarchive *ia, const char *name,\n        struct buffer *b)\n{\n    struct buff_struct *priv = ia->priv;\n    int rc = ia_deserialize_int(ia, \"len\", &b->len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < b->len) {\n        return -E2BIG;\n    }\n    // set the buffer to null\n    if (b->len == -1) {\n       b->buff = NULL;\n       return rc;\n    }\n    b->buff = malloc(b->len);\n    if (!b->buff) {\n        return -ENOMEM;\n    }\n    memcpy(b->buff, priv->buffer+priv->off, b->len);\n    priv->off += b->len;\n    return 0;\n}\nint ia_deserialize_string(struct iarchive *ia, const char *name, char **s)\n{\n    struct buff_struct *priv = ia->priv;\n    int32_t len;\n    int rc = ia_deserialize_int(ia, \"len\", &len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < len) {\n        return -E2BIG;\n    }\n    if (len < 0) {\n        return -EINVAL;\n    }\n    *s = malloc(len+1);\n    if (!*s) {\n        return -ENOMEM;\n    }\n    memcpy(*s, priv->buffer+priv->off, len);\n    (*s)[len] = '\\0';\n    priv->off += len;\n    return 0;\n}\n\nstatic struct iarchive ia_default = { STRUCT_INITIALIZER (start_record ,ia_start_record),\n        STRUCT_INITIALIZER (end_record ,ia_end_record), STRUCT_INITIALIZER (start_vector , ia_start_vector),\n        STRUCT_INITIALIZER (end_vector ,ia_end_vector), STRUCT_INITIALIZER (deserialize_Bool , ia_deserialize_bool),\n        STRUCT_INITIALIZER (deserialize_Int ,ia_deserialize_int),\n        STRUCT_INITIALIZER (deserialize_Long , ia_deserialize_long) ,\n        STRUCT_INITIALIZER (deserialize_Buffer, ia_deserialize_buffer),\n        STRUCT_INITIALIZER (deserialize_String, ia_deserialize_string)   };\n\nstatic struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_start_record),\n        STRUCT_INITIALIZER (end_record , oa_end_record), STRUCT_INITIALIZER (start_vector , oa_start_vector),\n        STRUCT_INITIALIZER (end_vector , oa_end_vector), STRUCT_INITIALIZER (serialize_Bool , oa_serialize_bool),\n        STRUCT_INITIALIZER (serialize_Int , oa_serialize_int),\n        STRUCT_INITIALIZER (serialize_Long , oa_serialize_long) ,\n        STRUCT_INITIALIZER (serialize_Buffer , oa_serialize_buffer),\n        STRUCT_INITIALIZER (serialize_String , oa_serialize_string) };\n\nstruct iarchive *create_buffer_iarchive(char *buffer, int len)\n{\n    struct iarchive *ia = malloc(sizeof(*ia));\n    struct buff_struct *buff = malloc(sizeof(struct buff_struct));\n    if (!ia) return 0;\n    if (!buff) {\n        free(ia);\n        return 0;\n    }\n    *ia = ia_default;\n    buff->off = 0;\n    buff->buffer = buffer;\n    buff->len = len;\n    ia->priv = buff;\n    return ia;\n}\n\nstruct oarchive *create_buffer_oarchive()\n{\n    struct oarchive *oa = malloc(sizeof(*oa));\n    struct buff_struct *buff = malloc(sizeof(struct buff_struct));\n    if (!oa) return 0;\n    if (!buff) {\n        free(oa);\n        return 0;\n    }\n    *oa = oa_default;\n    buff->off = 0;\n    buff->buffer = malloc(128);\n    buff->len = 128;\n    oa->priv = buff;\n    return oa;\n}\n\nvoid close_buffer_iarchive(struct iarchive **ia)\n{\n    free((*ia)->priv);\n    free(*ia);\n    *ia = 0;\n}\n\nvoid close_buffer_oarchive(struct oarchive **oa, int free_buffer)\n{\n    if (free_buffer) {\n        struct buff_struct *buff = (struct buff_struct *)(*oa)->priv;\n        if (buff->buffer) {\n            free(buff->buffer);\n        }\n    }\n    free((*oa)->priv);\n    free(*oa);\n    *oa = 0;\n}\n\nchar *get_buffer(struct oarchive *oa)\n{\n    struct buff_struct *buff = oa->priv;\n    return buff->buffer;\n}\nint get_buffer_len(struct oarchive *oa)\n{\n    struct buff_struct *buff = oa->priv;\n    return buff->off;\n}\n"
  },
  {
    "path": "QSentinel/zookeeper/recordio.h",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#ifndef __RECORDIO_H__\n#define __RECORDIO_H__\n\n#include <sys/types.h>\n#define STRUCT_INITIALIZER(l,r) .l = r\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct buffer {\n    int32_t len;\n    char *buff;\n};\n\nvoid deallocate_String(char **s);\nvoid deallocate_Buffer(struct buffer *b);\nvoid deallocate_vector(void *d);\nstruct iarchive {\n    int (*start_record)(struct iarchive *ia, const char *tag);\n    int (*end_record)(struct iarchive *ia, const char *tag);\n    int (*start_vector)(struct iarchive *ia, const char *tag, int32_t *count);\n    int (*end_vector)(struct iarchive *ia, const char *tag);\n    int (*deserialize_Bool)(struct iarchive *ia, const char *name, int32_t *);\n    int (*deserialize_Int)(struct iarchive *ia, const char *name, int32_t *);\n    int (*deserialize_Long)(struct iarchive *ia, const char *name, int64_t *);\n    int (*deserialize_Buffer)(struct iarchive *ia, const char *name,\n            struct buffer *);\n    int (*deserialize_String)(struct iarchive *ia, const char *name, char **);\n    void *priv;\n};\nstruct oarchive {\n    int (*start_record)(struct oarchive *oa, const char *tag);\n    int (*end_record)(struct oarchive *oa, const char *tag);\n    int (*start_vector)(struct oarchive *oa, const char *tag, const int32_t *count);\n    int (*end_vector)(struct oarchive *oa, const char *tag);\n    int (*serialize_Bool)(struct oarchive *oa, const char *name, const int32_t *);\n    int (*serialize_Int)(struct oarchive *oa, const char *name, const int32_t *);\n    int (*serialize_Long)(struct oarchive *oa, const char *name,\n            const int64_t *);\n    int (*serialize_Buffer)(struct oarchive *oa, const char *name,\n            const struct buffer *);\n    int (*serialize_String)(struct oarchive *oa, const char *name, char **);\n    void *priv;\n};\n\nstruct oarchive *create_buffer_oarchive(void);\nvoid close_buffer_oarchive(struct oarchive **oa, int free_buffer);\nstruct iarchive *create_buffer_iarchive(char *buffer, int len);\nvoid close_buffer_iarchive(struct iarchive **ia);\nchar *get_buffer(struct oarchive *);\nint get_buffer_len(struct oarchive *);\n\n#if defined(__APPLE__)\n#else\nint64_t htonll(int64_t v);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "QSentinel/zookeeper/zookeeper.jute.c",
    "content": "/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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#include <stdlib.h>\n#include \"zookeeper.jute.h\"\n\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_String(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_String(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Id(struct Id*v){\n    deallocate_String(&v->scheme);\n    deallocate_String(&v->id);\n}\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"perms\", &v->perms);\n    rc = rc ? rc : serialize_Id(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"perms\", &v->perms);\n    rc = rc ? rc : deserialize_Id(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ACL(struct ACL*v){\n    deallocate_Id(&v->id);\n}\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Int(out, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : out->serialize_Int(out, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Int(in, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : in->deserialize_Int(in, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Stat(struct Stat*v){\n}\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_StatPersisted(struct StatPersisted*v){\n}\nint serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_StatPersistedV1(struct StatPersistedV1*v){\n}\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Long(out, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Long(in, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectRequest(struct ConnectRequest*v){\n    deallocate_Buffer(&v->passwd);\n}\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectResponse(struct ConnectResponse*v){\n    deallocate_Buffer(&v->passwd);\n}\nint allocate_String_vector(struct String_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_String_vector(struct String_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_String(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : out->serialize_String(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : in->deserialize_String(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : serialize_String_vector(out, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : deserialize_String_vector(in, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetWatches(struct SetWatches*v){\n    deallocate_String_vector(&v->dataWatches);\n    deallocate_String_vector(&v->existWatches);\n    deallocate_String_vector(&v->childWatches);\n}\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_RequestHeader(struct RequestHeader*v){\n}\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Bool(out, \"done\", &v->done);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Bool(in, \"done\", &v->done);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiHeader(struct MultiHeader*v){\n}\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_Buffer(out, \"auth\", &v->auth);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"auth\", &v->auth);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_AuthPacket(struct AuthPacket*v){\n    deallocate_String(&v->scheme);\n    deallocate_Buffer(&v->auth);\n}\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ReplyHeader(struct ReplyHeader*v){\n}\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataRequest(struct GetDataRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataRequest(struct SetDataRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataResponse(struct SetDataResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*v){\n    deallocate_Buffer(&v->token);\n}\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_ACL_vector(struct ACL_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_ACL(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_ACL(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_ACL(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"flags\", &v->flags);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"flags\", &v->flags);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateRequest(struct CreateRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteRequest(struct DeleteRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*v){\n    deallocate_String(&v->path);\n}\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*v){\n}\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncRequest(struct SyncRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncResponse(struct SyncResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLRequest(struct GetACLRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLRequest(struct SetACLRequest*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLResponse(struct SetACLResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Int(out, \"state\", &v->state);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Int(in, \"state\", &v->state);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_WatcherEvent(struct WatcherEvent*v){\n    deallocate_String(&v->path);\n}\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorResponse(struct ErrorResponse*v){\n}\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateResponse(struct CreateResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsRequest(struct ExistsRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsResponse(struct ExistsResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataResponse(struct GetDataResponse*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*v){\n    deallocate_String_vector(&v->children);\n}\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*v){\n    deallocate_String_vector(&v->children);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLResponse(struct GetACLResponse*v){\n    deallocate_ACL_vector(&v->acl);\n    deallocate_Stat(&v->stat);\n}\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"serverid\", &v->serverid);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"serverid\", &v->serverid);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_LearnerInfo(struct LearnerInfo*v){\n}\nint allocate_Id_vector(struct Id_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Id_vector(struct Id_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Id(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Id(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Id(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Id_vector(out, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Id_vector(in, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_QuorumPacket(struct QuorumPacket*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Id_vector(&v->authinfo);\n}\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"magic\", &v->magic);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Long(out, \"dbid\", &v->dbid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"magic\", &v->magic);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Long(in, \"dbid\", &v->dbid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_FileHeader(struct FileHeader*v){\n}\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"clientId\", &v->clientId);\n    rc = rc ? rc : out->serialize_Int(out, \"cxid\", &v->cxid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Long(out, \"time\", &v->time);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"clientId\", &v->clientId);\n    rc = rc ? rc : in->deserialize_Int(in, \"cxid\", &v->cxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"time\", &v->time);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_TxnHeader(struct TxnHeader*v){\n}\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->serialize_Int(out, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->deserialize_Int(in, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxn(struct CreateTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteTxn(struct DeleteTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataTxn(struct SetDataTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLTxn(struct SetACLTxn*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*v){\n}\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorTxn(struct ErrorTxn*v){\n}\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Txn(struct Txn*v){\n    deallocate_Buffer(&v->data);\n}\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Txn_vector(struct Txn_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Txn(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Txn(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Txn(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Txn_vector(out, \"txns\", &v->txns);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Txn_vector(in, \"txns\", &v->txns);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiTxn(struct MultiTxn*v){\n    deallocate_Txn_vector(&v->txns);\n}\n"
  },
  {
    "path": "QSentinel/zookeeper/zookeeper.jute.h",
    "content": "/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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 __ZOOKEEPER_JUTE__\n#define __ZOOKEEPER_JUTE__\n#include \"recordio.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct Id {\n    char * scheme;\n    char * id;\n};\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v);\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v);\nvoid deallocate_Id(struct Id*);\nstruct ACL {\n    int32_t perms;\n    struct Id id;\n};\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v);\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v);\nvoid deallocate_ACL(struct ACL*);\nstruct Stat {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int32_t dataLength;\n    int32_t numChildren;\n    int64_t pzxid;\n};\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v);\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v);\nvoid deallocate_Stat(struct Stat*);\nstruct StatPersisted {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int64_t pzxid;\n};\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v);\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v);\nvoid deallocate_StatPersisted(struct StatPersisted*);\nstruct StatPersistedV1 {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n};\nint serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v);\nint deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v);\nvoid deallocate_StatPersistedV1(struct StatPersistedV1*);\nstruct ConnectRequest {\n    int32_t protocolVersion;\n    int64_t lastZxidSeen;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v);\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v);\nvoid deallocate_ConnectRequest(struct ConnectRequest*);\nstruct ConnectResponse {\n    int32_t protocolVersion;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v);\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v);\nvoid deallocate_ConnectResponse(struct ConnectResponse*);\nstruct String_vector {\n    int32_t count;\n    char * *data;\n\n};\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v);\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v);\nint allocate_String_vector(struct String_vector *v, int32_t len);\nint deallocate_String_vector(struct String_vector *v);\nstruct SetWatches {\n    int64_t relativeZxid;\n    struct String_vector dataWatches;\n    struct String_vector existWatches;\n    struct String_vector childWatches;\n};\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v);\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v);\nvoid deallocate_SetWatches(struct SetWatches*);\nstruct RequestHeader {\n    int32_t xid;\n    int32_t type;\n};\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v);\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v);\nvoid deallocate_RequestHeader(struct RequestHeader*);\nstruct MultiHeader {\n    int32_t type;\n    int32_t done;\n    int32_t err;\n};\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v);\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v);\nvoid deallocate_MultiHeader(struct MultiHeader*);\nstruct AuthPacket {\n    int32_t type;\n    char * scheme;\n    struct buffer auth;\n};\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v);\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v);\nvoid deallocate_AuthPacket(struct AuthPacket*);\nstruct ReplyHeader {\n    int32_t xid;\n    int64_t zxid;\n    int32_t err;\n};\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v);\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v);\nvoid deallocate_ReplyHeader(struct ReplyHeader*);\nstruct GetDataRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v);\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v);\nvoid deallocate_GetDataRequest(struct GetDataRequest*);\nstruct SetDataRequest {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v);\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v);\nvoid deallocate_SetDataRequest(struct SetDataRequest*);\nstruct SetDataResponse {\n    struct Stat stat;\n};\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v);\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v);\nvoid deallocate_SetDataResponse(struct SetDataResponse*);\nstruct GetSASLRequest {\n    struct buffer token;\n};\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v);\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v);\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*);\nstruct SetSASLRequest {\n    struct buffer token;\n};\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v);\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v);\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*);\nstruct SetSASLResponse {\n    struct buffer token;\n};\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v);\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v);\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*);\nstruct ACL_vector {\n    int32_t count;\n    struct ACL *data;\n\n};\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v);\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v);\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len);\nint deallocate_ACL_vector(struct ACL_vector *v);\nstruct CreateRequest {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t flags;\n};\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v);\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v);\nvoid deallocate_CreateRequest(struct CreateRequest*);\nstruct DeleteRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v);\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v);\nvoid deallocate_DeleteRequest(struct DeleteRequest*);\nstruct GetChildrenRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v);\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v);\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*);\nstruct GetChildren2Request {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v);\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v);\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*);\nstruct CheckVersionRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v);\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v);\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*);\nstruct GetMaxChildrenRequest {\n    char * path;\n};\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v);\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v);\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*);\nstruct GetMaxChildrenResponse {\n    int32_t max;\n};\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v);\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v);\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*);\nstruct SetMaxChildrenRequest {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v);\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v);\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*);\nstruct SyncRequest {\n    char * path;\n};\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v);\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v);\nvoid deallocate_SyncRequest(struct SyncRequest*);\nstruct SyncResponse {\n    char * path;\n};\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v);\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v);\nvoid deallocate_SyncResponse(struct SyncResponse*);\nstruct GetACLRequest {\n    char * path;\n};\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v);\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v);\nvoid deallocate_GetACLRequest(struct GetACLRequest*);\nstruct SetACLRequest {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v);\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v);\nvoid deallocate_SetACLRequest(struct SetACLRequest*);\nstruct SetACLResponse {\n    struct Stat stat;\n};\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v);\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v);\nvoid deallocate_SetACLResponse(struct SetACLResponse*);\nstruct WatcherEvent {\n    int32_t type;\n    int32_t state;\n    char * path;\n};\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v);\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v);\nvoid deallocate_WatcherEvent(struct WatcherEvent*);\nstruct ErrorResponse {\n    int32_t err;\n};\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v);\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v);\nvoid deallocate_ErrorResponse(struct ErrorResponse*);\nstruct CreateResponse {\n    char * path;\n};\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v);\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v);\nvoid deallocate_CreateResponse(struct CreateResponse*);\nstruct ExistsRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v);\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v);\nvoid deallocate_ExistsRequest(struct ExistsRequest*);\nstruct ExistsResponse {\n    struct Stat stat;\n};\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v);\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v);\nvoid deallocate_ExistsResponse(struct ExistsResponse*);\nstruct GetDataResponse {\n    struct buffer data;\n    struct Stat stat;\n};\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v);\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v);\nvoid deallocate_GetDataResponse(struct GetDataResponse*);\nstruct GetChildrenResponse {\n    struct String_vector children;\n};\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v);\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v);\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*);\nstruct GetChildren2Response {\n    struct String_vector children;\n    struct Stat stat;\n};\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v);\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v);\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*);\nstruct GetACLResponse {\n    struct ACL_vector acl;\n    struct Stat stat;\n};\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v);\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v);\nvoid deallocate_GetACLResponse(struct GetACLResponse*);\nstruct LearnerInfo {\n    int64_t serverid;\n    int32_t protocolVersion;\n};\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v);\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v);\nvoid deallocate_LearnerInfo(struct LearnerInfo*);\nstruct Id_vector {\n    int32_t count;\n    struct Id *data;\n\n};\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v);\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v);\nint allocate_Id_vector(struct Id_vector *v, int32_t len);\nint deallocate_Id_vector(struct Id_vector *v);\nstruct QuorumPacket {\n    int32_t type;\n    int64_t zxid;\n    struct buffer data;\n    struct Id_vector authinfo;\n};\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v);\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v);\nvoid deallocate_QuorumPacket(struct QuorumPacket*);\nstruct FileHeader {\n    int32_t magic;\n    int32_t version;\n    int64_t dbid;\n};\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v);\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v);\nvoid deallocate_FileHeader(struct FileHeader*);\nstruct TxnHeader {\n    int64_t clientId;\n    int32_t cxid;\n    int64_t zxid;\n    int64_t time;\n    int32_t type;\n};\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v);\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v);\nvoid deallocate_TxnHeader(struct TxnHeader*);\nstruct CreateTxnV0 {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n};\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v);\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v);\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*);\nstruct CreateTxn {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n    int32_t parentCVersion;\n};\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v);\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v);\nvoid deallocate_CreateTxn(struct CreateTxn*);\nstruct DeleteTxn {\n    char * path;\n};\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v);\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v);\nvoid deallocate_DeleteTxn(struct DeleteTxn*);\nstruct SetDataTxn {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v);\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v);\nvoid deallocate_SetDataTxn(struct SetDataTxn*);\nstruct CheckVersionTxn {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v);\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v);\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*);\nstruct SetACLTxn {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v);\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v);\nvoid deallocate_SetACLTxn(struct SetACLTxn*);\nstruct SetMaxChildrenTxn {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v);\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v);\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*);\nstruct CreateSessionTxn {\n    int32_t timeOut;\n};\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v);\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v);\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*);\nstruct ErrorTxn {\n    int32_t err;\n};\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v);\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v);\nvoid deallocate_ErrorTxn(struct ErrorTxn*);\nstruct Txn {\n    int32_t type;\n    struct buffer data;\n};\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v);\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v);\nvoid deallocate_Txn(struct Txn*);\nstruct Txn_vector {\n    int32_t count;\n    struct Txn *data;\n\n};\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v);\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v);\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len);\nint deallocate_Txn_vector(struct Txn_vector *v);\nstruct MultiTxn {\n    struct Txn_vector txns;\n};\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v);\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v);\nvoid deallocate_MultiTxn(struct MultiTxn*);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif //ZOOKEEPER_JUTE__\n"
  },
  {
    "path": "QSentinel/zookeeper/zookeeper_version.h",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#ifndef ZOOKEEPER_VERSION_H_\n#define ZOOKEEPER_VERSION_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ZOO_MAJOR_VERSION 3\n#define ZOO_MINOR_VERSION 4\n#define ZOO_PATCH_VERSION 6 \n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* ZOOKEEPER_VERSION_H_ */\n"
  },
  {
    "path": "Qedis.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tBA1250711A6D43D500A0532D /* MemoryFile.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA12506F1A6D43D500A0532D /* MemoryFile.cc */; };\n\t\tBA1250721A6D43D500A0532D /* MemoryFile.h in Headers */ = {isa = PBXBuildFile; fileRef = BA1250701A6D43D500A0532D /* MemoryFile.h */; };\n\t\tBA1250811A7338CD00A0532D /* AsyncBuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA12507F1A7338CD00A0532D /* AsyncBuffer.cc */; };\n\t\tBA1250821A7338CD00A0532D /* AsyncBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA1250801A7338CD00A0532D /* AsyncBuffer.h */; };\n\t\tBA29C44E19FB2CEE00796E3A /* QMulti.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA29C44C19FB2CEE00796E3A /* QMulti.cc */; };\n\t\tBA29C44F19FB2CEE00796E3A /* QMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = BA29C44D19FB2CEE00796E3A /* QMulti.h */; };\n\t\tBA2C0C7B1CDC1DC800BDF050 /* Delegate.h in Headers */ = {isa = PBXBuildFile; fileRef = BA2C0C7A1CDC1DC800BDF050 /* Delegate.h */; };\n\t\tBA34F9741D5C199D00F1DE01 /* builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E31D5C199D00F1DE01 /* builder.cc */; };\n\t\tBA34F9751D5C199D00F1DE01 /* builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8E41D5C199D00F1DE01 /* builder.h */; };\n\t\tBA34F9761D5C199D00F1DE01 /* c.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E51D5C199D00F1DE01 /* c.cc */; };\n\t\tBA34F9791D5C199D00F1DE01 /* db_bench.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E81D5C199D00F1DE01 /* db_bench.cc */; };\n\t\tBA34F97A1D5C199D00F1DE01 /* db_impl.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E91D5C199D00F1DE01 /* db_impl.cc */; };\n\t\tBA34F97B1D5C199D00F1DE01 /* db_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EA1D5C199D00F1DE01 /* db_impl.h */; };\n\t\tBA34F97C1D5C199D00F1DE01 /* db_iter.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8EB1D5C199D00F1DE01 /* db_iter.cc */; };\n\t\tBA34F97D1D5C199D00F1DE01 /* db_iter.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EC1D5C199D00F1DE01 /* db_iter.h */; };\n\t\tBA34F97F1D5C199D00F1DE01 /* dbformat.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8EE1D5C199D00F1DE01 /* dbformat.cc */; };\n\t\tBA34F9801D5C199D00F1DE01 /* dbformat.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EF1D5C199D00F1DE01 /* dbformat.h */; };\n\t\tBA34F9821D5C199D00F1DE01 /* dumpfile.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F11D5C199D00F1DE01 /* dumpfile.cc */; };\n\t\tBA34F9841D5C199D00F1DE01 /* filename.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F31D5C199D00F1DE01 /* filename.cc */; };\n\t\tBA34F9851D5C199D00F1DE01 /* filename.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F41D5C199D00F1DE01 /* filename.h */; };\n\t\tBA34F9881D5C199D00F1DE01 /* log_format.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F71D5C199D00F1DE01 /* log_format.h */; };\n\t\tBA34F9891D5C199D00F1DE01 /* log_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F81D5C199D00F1DE01 /* log_reader.cc */; };\n\t\tBA34F98A1D5C199D00F1DE01 /* log_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F91D5C199D00F1DE01 /* log_reader.h */; };\n\t\tBA34F98C1D5C199D00F1DE01 /* log_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FB1D5C199D00F1DE01 /* log_writer.cc */; };\n\t\tBA34F98D1D5C199D00F1DE01 /* log_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8FC1D5C199D00F1DE01 /* log_writer.h */; };\n\t\tBA34F98E1D5C199D00F1DE01 /* memtable.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FD1D5C199D00F1DE01 /* memtable.cc */; };\n\t\tBA34F98F1D5C199D00F1DE01 /* memtable.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8FE1D5C199D00F1DE01 /* memtable.h */; };\n\t\tBA34F9901D5C199D00F1DE01 /* repair.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FF1D5C199D00F1DE01 /* repair.cc */; };\n\t\tBA34F9911D5C199D00F1DE01 /* skiplist.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9001D5C199D00F1DE01 /* skiplist.h */; };\n\t\tBA34F9931D5C199D00F1DE01 /* snapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9021D5C199D00F1DE01 /* snapshot.h */; };\n\t\tBA34F9941D5C199D00F1DE01 /* table_cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9031D5C199D00F1DE01 /* table_cache.cc */; };\n\t\tBA34F9951D5C199D00F1DE01 /* table_cache.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9041D5C199D00F1DE01 /* table_cache.h */; };\n\t\tBA34F9961D5C199D00F1DE01 /* version_edit.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9051D5C199D00F1DE01 /* version_edit.cc */; };\n\t\tBA34F9971D5C199D00F1DE01 /* version_edit.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9061D5C199D00F1DE01 /* version_edit.h */; };\n\t\tBA34F9991D5C199D00F1DE01 /* version_set.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9081D5C199D00F1DE01 /* version_set.cc */; };\n\t\tBA34F99A1D5C199D00F1DE01 /* version_set.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9091D5C199D00F1DE01 /* version_set.h */; };\n\t\tBA34F99C1D5C199D00F1DE01 /* write_batch.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F90B1D5C199D00F1DE01 /* write_batch.cc */; };\n\t\tBA34F99D1D5C199D00F1DE01 /* write_batch_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */; };\n\t\tBA34F9A11D5C199D00F1DE01 /* memenv.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F91A1D5C199D00F1DE01 /* memenv.cc */; };\n\t\tBA34F9A21D5C199D00F1DE01 /* memenv.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F91B1D5C199D00F1DE01 /* memenv.h */; };\n\t\tBA34F9A41D5C199D00F1DE01 /* c.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F91F1D5C199D00F1DE01 /* c.h */; };\n\t\tBA34F9A51D5C199D00F1DE01 /* cache.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9201D5C199D00F1DE01 /* cache.h */; };\n\t\tBA34F9A61D5C199D00F1DE01 /* comparator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9211D5C199D00F1DE01 /* comparator.h */; };\n\t\tBA34F9A71D5C199D00F1DE01 /* db.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9221D5C199D00F1DE01 /* db.h */; };\n\t\tBA34F9A81D5C199D00F1DE01 /* dumpfile.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9231D5C199D00F1DE01 /* dumpfile.h */; };\n\t\tBA34F9A91D5C199D00F1DE01 /* env.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9241D5C199D00F1DE01 /* env.h */; };\n\t\tBA34F9AA1D5C199D00F1DE01 /* filter_policy.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9251D5C199D00F1DE01 /* filter_policy.h */; };\n\t\tBA34F9AB1D5C199D00F1DE01 /* iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9261D5C199D00F1DE01 /* iterator.h */; };\n\t\tBA34F9AC1D5C199D00F1DE01 /* options.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9271D5C199D00F1DE01 /* options.h */; };\n\t\tBA34F9AD1D5C199D00F1DE01 /* slice.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9281D5C199D00F1DE01 /* slice.h */; };\n\t\tBA34F9AE1D5C199D00F1DE01 /* status.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9291D5C199D00F1DE01 /* status.h */; };\n\t\tBA34F9AF1D5C199D00F1DE01 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92A1D5C199D00F1DE01 /* table.h */; };\n\t\tBA34F9B01D5C199D00F1DE01 /* table_builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92B1D5C199D00F1DE01 /* table_builder.h */; };\n\t\tBA34F9B11D5C199D00F1DE01 /* write_batch.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92C1D5C199D00F1DE01 /* write_batch.h */; };\n\t\tBA34F9B51D5C199D00F1DE01 /* atomic_pointer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9321D5C199D00F1DE01 /* atomic_pointer.h */; };\n\t\tBA34F9B61D5C199D00F1DE01 /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9331D5C199D00F1DE01 /* port.h */; };\n\t\tBA34F9B71D5C199D00F1DE01 /* port_example.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9341D5C199D00F1DE01 /* port_example.h */; };\n\t\tBA34F9B81D5C199D00F1DE01 /* port_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9351D5C199D00F1DE01 /* port_posix.cc */; };\n\t\tBA34F9B91D5C199D00F1DE01 /* port_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9361D5C199D00F1DE01 /* port_posix.h */; };\n\t\tBA34F9BA1D5C199D00F1DE01 /* thread_annotations.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9381D5C199D00F1DE01 /* thread_annotations.h */; };\n\t\tBA34F9BD1D5C199D00F1DE01 /* block.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F93D1D5C199D00F1DE01 /* block.cc */; };\n\t\tBA34F9BE1D5C199D00F1DE01 /* block.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F93E1D5C199D00F1DE01 /* block.h */; };\n\t\tBA34F9BF1D5C199D00F1DE01 /* block_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F93F1D5C199D00F1DE01 /* block_builder.cc */; };\n\t\tBA34F9C01D5C199D00F1DE01 /* block_builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9401D5C199D00F1DE01 /* block_builder.h */; };\n\t\tBA34F9C11D5C199D00F1DE01 /* filter_block.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9411D5C199D00F1DE01 /* filter_block.cc */; };\n\t\tBA34F9C21D5C199D00F1DE01 /* filter_block.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9421D5C199D00F1DE01 /* filter_block.h */; };\n\t\tBA34F9C41D5C199D00F1DE01 /* format.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9441D5C199D00F1DE01 /* format.cc */; };\n\t\tBA34F9C51D5C199D00F1DE01 /* format.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9451D5C199D00F1DE01 /* format.h */; };\n\t\tBA34F9C61D5C199D00F1DE01 /* iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9461D5C199D00F1DE01 /* iterator.cc */; };\n\t\tBA34F9C71D5C199D00F1DE01 /* iterator_wrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */; };\n\t\tBA34F9C81D5C199D00F1DE01 /* merger.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9481D5C199D00F1DE01 /* merger.cc */; };\n\t\tBA34F9C91D5C199D00F1DE01 /* merger.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9491D5C199D00F1DE01 /* merger.h */; };\n\t\tBA34F9CA1D5C199D00F1DE01 /* table.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94A1D5C199D00F1DE01 /* table.cc */; };\n\t\tBA34F9CB1D5C199D00F1DE01 /* table_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94B1D5C199D00F1DE01 /* table_builder.cc */; };\n\t\tBA34F9CD1D5C199D00F1DE01 /* two_level_iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */; };\n\t\tBA34F9CE1D5C199D00F1DE01 /* two_level_iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */; };\n\t\tBA34F9CF1D5C199D00F1DE01 /* arena.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9511D5C199D00F1DE01 /* arena.cc */; };\n\t\tBA34F9D01D5C199D00F1DE01 /* arena.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9521D5C199D00F1DE01 /* arena.h */; };\n\t\tBA34F9D21D5C199D00F1DE01 /* bloom.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9541D5C199D00F1DE01 /* bloom.cc */; };\n\t\tBA34F9D41D5C199D00F1DE01 /* cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9561D5C199D00F1DE01 /* cache.cc */; };\n\t\tBA34F9D61D5C199D00F1DE01 /* coding.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9581D5C199D00F1DE01 /* coding.cc */; };\n\t\tBA34F9D71D5C199D00F1DE01 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9591D5C199D00F1DE01 /* coding.h */; };\n\t\tBA34F9D91D5C199D00F1DE01 /* comparator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95B1D5C199D00F1DE01 /* comparator.cc */; };\n\t\tBA34F9DA1D5C199D00F1DE01 /* crc32c.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95C1D5C199D00F1DE01 /* crc32c.cc */; };\n\t\tBA34F9DB1D5C199D00F1DE01 /* crc32c.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F95D1D5C199D00F1DE01 /* crc32c.h */; };\n\t\tBA34F9DD1D5C199D00F1DE01 /* env.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95F1D5C199D00F1DE01 /* env.cc */; };\n\t\tBA34F9DE1D5C199D00F1DE01 /* env_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9601D5C199D00F1DE01 /* env_posix.cc */; };\n\t\tBA34F9E01D5C199D00F1DE01 /* filter_policy.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9621D5C199D00F1DE01 /* filter_policy.cc */; };\n\t\tBA34F9E11D5C199D00F1DE01 /* hash.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9631D5C199D00F1DE01 /* hash.cc */; };\n\t\tBA34F9E21D5C199D00F1DE01 /* hash.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9641D5C199D00F1DE01 /* hash.h */; };\n\t\tBA34F9E41D5C199D00F1DE01 /* histogram.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9661D5C199D00F1DE01 /* histogram.cc */; };\n\t\tBA34F9E51D5C199D00F1DE01 /* histogram.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9671D5C199D00F1DE01 /* histogram.h */; };\n\t\tBA34F9E61D5C199D00F1DE01 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9681D5C199D00F1DE01 /* logging.cc */; };\n\t\tBA34F9E71D5C199D00F1DE01 /* logging.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9691D5C199D00F1DE01 /* logging.h */; };\n\t\tBA34F9E81D5C199D00F1DE01 /* mutexlock.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96A1D5C199D00F1DE01 /* mutexlock.h */; };\n\t\tBA34F9E91D5C199D00F1DE01 /* options.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96B1D5C199D00F1DE01 /* options.cc */; };\n\t\tBA34F9EA1D5C199D00F1DE01 /* posix_logger.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96C1D5C199D00F1DE01 /* posix_logger.h */; };\n\t\tBA34F9EB1D5C199D00F1DE01 /* random.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96D1D5C199D00F1DE01 /* random.h */; };\n\t\tBA34F9EC1D5C199D00F1DE01 /* status.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96E1D5C199D00F1DE01 /* status.cc */; };\n\t\tBA34F9ED1D5C199D00F1DE01 /* testharness.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96F1D5C199D00F1DE01 /* testharness.cc */; };\n\t\tBA34F9EE1D5C199D00F1DE01 /* testharness.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9701D5C199D00F1DE01 /* testharness.h */; };\n\t\tBA34F9EF1D5C199D00F1DE01 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9711D5C199D00F1DE01 /* testutil.cc */; };\n\t\tBA34F9F01D5C199D00F1DE01 /* testutil.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9721D5C199D00F1DE01 /* testutil.h */; };\n\t\tBA34F9F21D5C221700F1DE01 /* libleveldb.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */; settings = {ATTRIBUTES = (Required, ); }; };\n\t\tBA34F9F31D5C243800F1DE01 /* libqedislib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */; };\n\t\tBA34F9F41D5C244E00F1DE01 /* libqbaselib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */; settings = {ATTRIBUTES = (Required, ); }; };\n\t\tBA34F9F81D5C25E400F1DE01 /* QDumpInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */; };\n\t\tBA34F9F91D5C25E400F1DE01 /* QLeveldb.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */; };\n\t\tBA34F9FA1D5C25E400F1DE01 /* QLeveldb.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9F71D5C25E400F1DE01 /* QLeveldb.h */; };\n\t\tBA34F9FB1D5C265D00F1DE01 /* libqbaselib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */; };\n\t\tBA34F9FC1D5C266200F1DE01 /* libleveldb.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */; };\n\t\tBA3B0AD61ACBA9E1000D9399 /* QAOF.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA3B0AD41ACBA9E1000D9399 /* QAOF.cc */; };\n\t\tBA3B0AD71ACBA9E1000D9399 /* QAOF.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3B0AD51ACBA9E1000D9399 /* QAOF.h */; };\n\t\tBA4053931BA1AE3B00B93053 /* QReplication.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4053911BA1AE3B00B93053 /* QReplication.cc */; };\n\t\tBA4053941BA1AE3B00B93053 /* QReplication.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4053921BA1AE3B00B93053 /* QReplication.h */; };\n\t\tBA47763D1AB0773F00EDBB5D /* redisIntset.c in Sources */ = {isa = PBXBuildFile; fileRef = BA47763B1AB0773F00EDBB5D /* redisIntset.c */; };\n\t\tBA47763E1AB0773F00EDBB5D /* redisIntset.h in Headers */ = {isa = PBXBuildFile; fileRef = BA47763C1AB0773F00EDBB5D /* redisIntset.h */; };\n\t\tBA4CD2261CEB54FF00F68F67 /* QHashModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */; };\n\t\tBA4CD2271CEB54FF00F68F67 /* QHashModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD21F1CEB54FF00F68F67 /* QHashModule.h */; };\n\t\tBA4CD2281CEB54FF00F68F67 /* QListModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2201CEB54FF00F68F67 /* QListModule.cc */; };\n\t\tBA4CD2291CEB54FF00F68F67 /* QListModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2211CEB54FF00F68F67 /* QListModule.h */; };\n\t\tBA4CD22A1CEB54FF00F68F67 /* QModuleInit.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */; };\n\t\tBA4CD22B1CEB54FF00F68F67 /* QModuleInit.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2231CEB54FF00F68F67 /* QModuleInit.h */; };\n\t\tBA4CD22C1CEB54FF00F68F67 /* QSetModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2241CEB54FF00F68F67 /* QSetModule.cc */; };\n\t\tBA4CD22D1CEB54FF00F68F67 /* QSetModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2251CEB54FF00F68F67 /* QSetModule.h */; };\n\t\tBA4CD2321CEB61E100F68F67 /* QModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2301CEB61E100F68F67 /* QModule.cc */; };\n\t\tBA4CD2331CEB61E100F68F67 /* QModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2311CEB61E100F68F67 /* QModule.h */; };\n\t\tBA6C336C19EA77B200AC1800 /* QGlobRegex.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6C336A19EA77B200AC1800 /* QGlobRegex.cc */; };\n\t\tBA6C336D19EA77B200AC1800 /* QGlobRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6C336B19EA77B200AC1800 /* QGlobRegex.h */; };\n\t\tBA6C336F19EA77C100AC1800 /* QGlobRegex_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */; };\n\t\tBA6C337119EC0D9900AC1800 /* QPubsub.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6C337019EC0D9900AC1800 /* QPubsub.h */; };\n\t\tBA6E478E1F99056000EFBE6F /* QSlaveClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E478C1F99056000EFBE6F /* QSlaveClient.cc */; };\n\t\tBA6E47951F9905E600EFBE6F /* QClusterClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47921F9905E600EFBE6F /* QClusterClient.cc */; };\n\t\tBA6E479E1F9905F500EFBE6F /* recordio.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47971F9905F500EFBE6F /* recordio.c */; };\n\t\tBA6E479F1F9905F500EFBE6F /* zookeeper.jute.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */; };\n\t\tBA6E47A01F9905F500EFBE6F /* ZookeeperConn.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */; };\n\t\tBA6E47AD1F99068A00EFBE6F /* QClusterClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47921F9905E600EFBE6F /* QClusterClient.cc */; };\n\t\tBA6E47AE1F99068A00EFBE6F /* recordio.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47971F9905F500EFBE6F /* recordio.c */; };\n\t\tBA6E47AF1F99068A00EFBE6F /* zookeeper.jute.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */; };\n\t\tBA6E47B01F99068A00EFBE6F /* ZookeeperConn.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */; };\n\t\tBA6E47B11F9906A400EFBE6F /* QClusterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47931F9905E600EFBE6F /* QClusterClient.h */; };\n\t\tBA6E47B21F9906A400EFBE6F /* QClusterInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47941F9905E600EFBE6F /* QClusterInterface.h */; };\n\t\tBA6E47B31F9906A400EFBE6F /* proto.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47961F9905F500EFBE6F /* proto.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\tBA6E47B41F9906A400EFBE6F /* recordio.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47981F9905F500EFBE6F /* recordio.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\tBA6E47B51F9906A400EFBE6F /* zookeeper_version.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47991F9905F500EFBE6F /* zookeeper_version.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\tBA6E47B61F9906A400EFBE6F /* zookeeper.jute.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\tBA6E47B71F9906A400EFBE6F /* ZookeeperConn.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */; settings = {ATTRIBUTES = (Private, ); }; };\n\t\tBA98F83D1B01B16600ABCBD9 /* QConfig.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA98F83B1B01B16600ABCBD9 /* QConfig.cc */; };\n\t\tBA98F83E1B01B16600ABCBD9 /* QConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = BA98F83C1B01B16600ABCBD9 /* QConfig.h */; };\n\t\tBA98F8411B1B539600ABCBD9 /* QSlowLog.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */; };\n\t\tBA98F8421B1B539600ABCBD9 /* QSlowLog.h in Headers */ = {isa = PBXBuildFile; fileRef = BA98F8401B1B539600ABCBD9 /* QSlowLog.h */; };\n\t\tBAB2265E2007B7B7005241F6 /* QMigration.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAB2265C2007B7B7005241F6 /* QMigration.cc */; };\n\t\tBAD0C5FF19E7A02C005B3784 /* QClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E319E7A02C005B3784 /* QClient.cc */; };\n\t\tBAD0C60019E7A02C005B3784 /* QClient.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E419E7A02C005B3784 /* QClient.h */; };\n\t\tBAD0C60119E7A02C005B3784 /* QCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E519E7A02C005B3784 /* QCommand.cc */; };\n\t\tBAD0C60219E7A02C005B3784 /* QCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E619E7A02C005B3784 /* QCommand.h */; };\n\t\tBAD0C60319E7A02C005B3784 /* QCommon.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E719E7A02C005B3784 /* QCommon.cc */; };\n\t\tBAD0C60419E7A02C005B3784 /* QCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E819E7A02C005B3784 /* QCommon.h */; };\n\t\tBAD0C60519E7A02C005B3784 /* QHash.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EA19E7A02C005B3784 /* QHash.cc */; };\n\t\tBAD0C60619E7A02C005B3784 /* QHash.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5EB19E7A02C005B3784 /* QHash.h */; };\n\t\tBAD0C60719E7A02C005B3784 /* QKeyCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EC19E7A02C005B3784 /* QKeyCommand.cc */; };\n\t\tBAD0C60819E7A02C005B3784 /* QList.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5ED19E7A02C005B3784 /* QList.cc */; };\n\t\tBAD0C60919E7A02C005B3784 /* QList.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5EE19E7A02C005B3784 /* QList.h */; };\n\t\tBAD0C60A19E7A02C005B3784 /* QPubsub.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EF19E7A02C005B3784 /* QPubsub.cc */; };\n\t\tBAD0C60B19E7A02C005B3784 /* QServerCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F019E7A02C005B3784 /* QServerCommand.cc */; };\n\t\tBAD0C60C19E7A02C005B3784 /* QSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F119E7A02C005B3784 /* QSet.cc */; };\n\t\tBAD0C60D19E7A02C005B3784 /* QSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F219E7A02C005B3784 /* QSet.h */; };\n\t\tBAD0C60E19E7A02C005B3784 /* QSortedSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F319E7A02C005B3784 /* QSortedSet.cc */; };\n\t\tBAD0C60F19E7A02C005B3784 /* QSortedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F419E7A02C005B3784 /* QSortedSet.h */; };\n\t\tBAD0C61219E7A02C005B3784 /* QStore.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F719E7A02C005B3784 /* QStore.cc */; };\n\t\tBAD0C61319E7A02C005B3784 /* QStore.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F819E7A02C005B3784 /* QStore.h */; };\n\t\tBAD0C61419E7A02C005B3784 /* QString.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F919E7A02C005B3784 /* QString.cc */; };\n\t\tBAD0C61519E7A02C005B3784 /* QString.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5FA19E7A02C005B3784 /* QString.h */; };\n\t\tBAD0C61619E7A02C005B3784 /* QHelper.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5FB19E7A02C005B3784 /* QHelper.cc */; };\n\t\tBAD0C61719E7A02C005B3784 /* QHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5FC19E7A02C005B3784 /* QHelper.h */; };\n\t\tBAD0C61A19E7A075005B3784 /* Qedis.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C61919E7A075005B3784 /* Qedis.cc */; };\n\t\tBAD0C62519E7A8DA005B3784 /* libqedislib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */; };\n\t\tBADC7D7C19D6BC960036858E /* UnitTest.cc in Sources */ = {isa = PBXBuildFile; fileRef = BADC7D7A19D6BC960036858E /* UnitTest.cc */; };\n\t\tBADD49B51CCA68BA0093257E /* QProtoParser.cc in Sources */ = {isa = PBXBuildFile; fileRef = BADD49B31CCA68BA0093257E /* QProtoParser.cc */; };\n\t\tBADD49B61CCA68BA0093257E /* QProtoParser.h in Headers */ = {isa = PBXBuildFile; fileRef = BADD49B41CCA68BA0093257E /* QProtoParser.h */; };\n\t\tBAFC847219C44AFD00C18B22 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840219C44AFD00C18B22 /* Buffer.h */; };\n\t\tBAFC847319C44AFD00C18B22 /* ClientSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840419C44AFD00C18B22 /* ClientSocket.cc */; };\n\t\tBAFC847419C44AFD00C18B22 /* ClientSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840519C44AFD00C18B22 /* ClientSocket.h */; };\n\t\tBAFC847519C44AFD00C18B22 /* ConfigParser.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840719C44AFD00C18B22 /* ConfigParser.cc */; };\n\t\tBAFC847619C44AFD00C18B22 /* ConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840819C44AFD00C18B22 /* ConfigParser.h */; };\n\t\tBAFC847719C44AFD00C18B22 /* EPoller.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840919C44AFD00C18B22 /* EPoller.cc */; };\n\t\tBAFC847819C44AFD00C18B22 /* EPoller.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840A19C44AFD00C18B22 /* EPoller.h */; };\n\t\tBAFC847919C44AFD00C18B22 /* Kqueue.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840B19C44AFD00C18B22 /* Kqueue.cc */; };\n\t\tBAFC847A19C44AFD00C18B22 /* Kqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840C19C44AFD00C18B22 /* Kqueue.h */; };\n\t\tBAFC847B19C44AFD00C18B22 /* ListenSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840D19C44AFD00C18B22 /* ListenSocket.cc */; };\n\t\tBAFC847C19C44AFD00C18B22 /* ListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840E19C44AFD00C18B22 /* ListenSocket.h */; };\n\t\tBAFC847D19C44AFD00C18B22 /* Logger.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC841019C44AFD00C18B22 /* Logger.cc */; };\n\t\tBAFC847E19C44AFD00C18B22 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC841119C44AFD00C18B22 /* Logger.h */; };\n\t\tBAFC848E19C44AFD00C18B22 /* NetThreadPool.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */; };\n\t\tBAFC848F19C44AFD00C18B22 /* NetThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842B19C44AFD00C18B22 /* NetThreadPool.h */; };\n\t\tBAFC849019C44AFD00C18B22 /* Poller.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842C19C44AFD00C18B22 /* Poller.h */; };\n\t\tBAFC849119C44AFD00C18B22 /* Server.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC842D19C44AFD00C18B22 /* Server.cc */; };\n\t\tBAFC849219C44AFD00C18B22 /* Server.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842E19C44AFD00C18B22 /* Server.h */; };\n\t\tBAFC84B719C44AFD00C18B22 /* Socket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846019C44AFD00C18B22 /* Socket.cc */; };\n\t\tBAFC84B819C44AFD00C18B22 /* Socket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846119C44AFD00C18B22 /* Socket.h */; };\n\t\tBAFC84B919C44AFD00C18B22 /* StreamSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846219C44AFD00C18B22 /* StreamSocket.cc */; };\n\t\tBAFC84BA19C44AFD00C18B22 /* StreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846319C44AFD00C18B22 /* StreamSocket.h */; };\n\t\tBAFC84BB19C44AFD00C18B22 /* TaskManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846419C44AFD00C18B22 /* TaskManager.cc */; };\n\t\tBAFC84BC19C44AFD00C18B22 /* TaskManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846519C44AFD00C18B22 /* TaskManager.h */; };\n\t\tBAFC84C219C44AFD00C18B22 /* ThreadPool.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846C19C44AFD00C18B22 /* ThreadPool.cc */; };\n\t\tBAFC84C319C44AFD00C18B22 /* ThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846D19C44AFD00C18B22 /* ThreadPool.h */; };\n\t\tBAFC84C419C44AFD00C18B22 /* Timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846E19C44AFD00C18B22 /* Timer.cc */; };\n\t\tBAFC84C519C44AFD00C18B22 /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846F19C44AFD00C18B22 /* Timer.h */; };\n\t\tBAFC84C619C44AFD00C18B22 /* UnboundedBuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */; };\n\t\tBAFC84C719C44AFD00C18B22 /* UnboundedBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */; };\n\t\tBAFFCB531A8F09CA00C47F92 /* QDB.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB511A8F09CA00C47F92 /* QDB.cc */; };\n\t\tBAFFCB541A8F09CA00C47F92 /* QDB.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB521A8F09CA00C47F92 /* QDB.h */; };\n\t\tBAFFCB5A1A9244B200C47F92 /* lzf_c.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB561A9244B200C47F92 /* lzf_c.c */; };\n\t\tBAFFCB5B1A9244B200C47F92 /* lzf_d.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB571A9244B200C47F92 /* lzf_d.c */; };\n\t\tBAFFCB5C1A9244B200C47F92 /* lzf.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB581A9244B200C47F92 /* lzf.h */; };\n\t\tBAFFCB5D1A9244B200C47F92 /* lzfP.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB591A9244B200C47F92 /* lzfP.h */; };\n\t\tBAFFCB5F1A9423DC00C47F92 /* crc64.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB5E1A9423DC00C47F92 /* crc64.c */; };\n\t\tBAFFCB621A9EA0AC00C47F92 /* redisZipList.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB601A9EA0AC00C47F92 /* redisZipList.c */; };\n\t\tBAFFCB631A9EA0AC00C47F92 /* redisZipList.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB611A9EA0AC00C47F92 /* redisZipList.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tBA34F8DB1D5C190900F1DE01 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BA34F6021D5C151700F1DE01;\n\t\t\tremoteInfo = leveldb;\n\t\t};\n\t\tBA34F8DD1D5C191300F1DE01 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BA34F6021D5C151700F1DE01;\n\t\t\tremoteInfo = leveldb;\n\t\t};\n\t\tBA4CD22E1CEB55B400F68F67 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAD0C5D819E79FF7005B3784;\n\t\t\tremoteInfo = qedislib;\n\t\t};\n\t\tBA6E47A91F99066700EFBE6F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAFC83FD19C44AE800C18B22;\n\t\t\tremoteInfo = qbaselib;\n\t\t};\n\t\tBA6E47AB1F99066700EFBE6F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAD0C5D819E79FF7005B3784;\n\t\t\tremoteInfo = qedislib;\n\t\t};\n\t\tBAD0C5DD19E7A002005B3784 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAFC83FD19C44AE800C18B22;\n\t\t\tremoteInfo = qbaselib;\n\t\t};\n\t\tBAD0C5DF19E7A007005B3784 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAD0C5D819E79FF7005B3784;\n\t\t\tremoteInfo = qedislib;\n\t\t};\n\t\tBAD0C62019E7A889005B3784 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAFC83FD19C44AE800C18B22;\n\t\t\tremoteInfo = qbaselib;\n\t\t};\n\t\tBAD0C62219E7A88D005B3784 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAD0C5D819E79FF7005B3784;\n\t\t\tremoteInfo = qedislib;\n\t\t};\n\t\tBAFC84D419C44B6800C18B22 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BABEBECD19C4474900010636 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BAFC83FD19C44AE800C18B22;\n\t\t\tremoteInfo = qbaselib;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tBABEBED319C4474900010636 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = /usr/share/man/man1/;\n\t\t\tdstSubfolderSpec = 0;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 1;\n\t\t};\n\t\tBADC7D6F19D6BC700036858E /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = /usr/share/man/man1/;\n\t\t\tdstSubfolderSpec = 0;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 1;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tBA12506F1A6D43D500A0532D /* MemoryFile.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryFile.cc; sourceTree = \"<group>\"; };\n\t\tBA1250701A6D43D500A0532D /* MemoryFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryFile.h; sourceTree = \"<group>\"; };\n\t\tBA12507F1A7338CD00A0532D /* AsyncBuffer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncBuffer.cc; path = QBase/AsyncBuffer.cc; sourceTree = \"<group>\"; };\n\t\tBA1250801A7338CD00A0532D /* AsyncBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncBuffer.h; path = QBase/AsyncBuffer.h; sourceTree = \"<group>\"; };\n\t\tBA29C44C19FB2CEE00796E3A /* QMulti.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QMulti.cc; path = QedisCore/QMulti.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA29C44D19FB2CEE00796E3A /* QMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QMulti.h; path = QedisCore/QMulti.h; sourceTree = SOURCE_ROOT; };\n\t\tBA2C0C7A1CDC1DC800BDF050 /* Delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Delegate.h; path = QBase/Delegate.h; sourceTree = \"<group>\"; };\n\t\tBA34F6031D5C151700F1DE01 /* libleveldb.dylib */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.dylib\"; includeInIndex = 0; path = libleveldb.dylib; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBA34F8E31D5C199D00F1DE01 /* builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builder.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8E41D5C199D00F1DE01 /* builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builder.h; sourceTree = \"<group>\"; };\n\t\tBA34F8E51D5C199D00F1DE01 /* c.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = c.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8E81D5C199D00F1DE01 /* db_bench.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_bench.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8E91D5C199D00F1DE01 /* db_impl.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_impl.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8EA1D5C199D00F1DE01 /* db_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_impl.h; sourceTree = \"<group>\"; };\n\t\tBA34F8EB1D5C199D00F1DE01 /* db_iter.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_iter.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8EC1D5C199D00F1DE01 /* db_iter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_iter.h; sourceTree = \"<group>\"; };\n\t\tBA34F8EE1D5C199D00F1DE01 /* dbformat.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dbformat.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8EF1D5C199D00F1DE01 /* dbformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dbformat.h; sourceTree = \"<group>\"; };\n\t\tBA34F8F11D5C199D00F1DE01 /* dumpfile.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dumpfile.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8F31D5C199D00F1DE01 /* filename.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filename.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8F41D5C199D00F1DE01 /* filename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filename.h; sourceTree = \"<group>\"; };\n\t\tBA34F8F71D5C199D00F1DE01 /* log_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_format.h; sourceTree = \"<group>\"; };\n\t\tBA34F8F81D5C199D00F1DE01 /* log_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_reader.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8F91D5C199D00F1DE01 /* log_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_reader.h; sourceTree = \"<group>\"; };\n\t\tBA34F8FB1D5C199D00F1DE01 /* log_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_writer.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8FC1D5C199D00F1DE01 /* log_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_writer.h; sourceTree = \"<group>\"; };\n\t\tBA34F8FD1D5C199D00F1DE01 /* memtable.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memtable.cc; sourceTree = \"<group>\"; };\n\t\tBA34F8FE1D5C199D00F1DE01 /* memtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memtable.h; sourceTree = \"<group>\"; };\n\t\tBA34F8FF1D5C199D00F1DE01 /* repair.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = repair.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9001D5C199D00F1DE01 /* skiplist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skiplist.h; sourceTree = \"<group>\"; };\n\t\tBA34F9021D5C199D00F1DE01 /* snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = snapshot.h; sourceTree = \"<group>\"; };\n\t\tBA34F9031D5C199D00F1DE01 /* table_cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table_cache.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9041D5C199D00F1DE01 /* table_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table_cache.h; sourceTree = \"<group>\"; };\n\t\tBA34F9051D5C199D00F1DE01 /* version_edit.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version_edit.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9061D5C199D00F1DE01 /* version_edit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version_edit.h; sourceTree = \"<group>\"; };\n\t\tBA34F9081D5C199D00F1DE01 /* version_set.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version_set.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9091D5C199D00F1DE01 /* version_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version_set.h; sourceTree = \"<group>\"; };\n\t\tBA34F90B1D5C199D00F1DE01 /* write_batch.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write_batch.cc; sourceTree = \"<group>\"; };\n\t\tBA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write_batch_internal.h; sourceTree = \"<group>\"; };\n\t\tBA34F91A1D5C199D00F1DE01 /* memenv.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memenv.cc; sourceTree = \"<group>\"; };\n\t\tBA34F91B1D5C199D00F1DE01 /* memenv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memenv.h; sourceTree = \"<group>\"; };\n\t\tBA34F91F1D5C199D00F1DE01 /* c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c.h; sourceTree = \"<group>\"; };\n\t\tBA34F9201D5C199D00F1DE01 /* cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cache.h; sourceTree = \"<group>\"; };\n\t\tBA34F9211D5C199D00F1DE01 /* comparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = comparator.h; sourceTree = \"<group>\"; };\n\t\tBA34F9221D5C199D00F1DE01 /* db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db.h; sourceTree = \"<group>\"; };\n\t\tBA34F9231D5C199D00F1DE01 /* dumpfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dumpfile.h; sourceTree = \"<group>\"; };\n\t\tBA34F9241D5C199D00F1DE01 /* env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = env.h; sourceTree = \"<group>\"; };\n\t\tBA34F9251D5C199D00F1DE01 /* filter_policy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter_policy.h; sourceTree = \"<group>\"; };\n\t\tBA34F9261D5C199D00F1DE01 /* iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = \"<group>\"; };\n\t\tBA34F9271D5C199D00F1DE01 /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = \"<group>\"; };\n\t\tBA34F9281D5C199D00F1DE01 /* slice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = slice.h; sourceTree = \"<group>\"; };\n\t\tBA34F9291D5C199D00F1DE01 /* status.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = status.h; sourceTree = \"<group>\"; };\n\t\tBA34F92A1D5C199D00F1DE01 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = \"<group>\"; };\n\t\tBA34F92B1D5C199D00F1DE01 /* table_builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table_builder.h; sourceTree = \"<group>\"; };\n\t\tBA34F92C1D5C199D00F1DE01 /* write_batch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write_batch.h; sourceTree = \"<group>\"; };\n\t\tBA34F9321D5C199D00F1DE01 /* atomic_pointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomic_pointer.h; sourceTree = \"<group>\"; };\n\t\tBA34F9331D5C199D00F1DE01 /* port.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port.h; sourceTree = \"<group>\"; };\n\t\tBA34F9341D5C199D00F1DE01 /* port_example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port_example.h; sourceTree = \"<group>\"; };\n\t\tBA34F9351D5C199D00F1DE01 /* port_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = port_posix.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9361D5C199D00F1DE01 /* port_posix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port_posix.h; sourceTree = \"<group>\"; };\n\t\tBA34F9381D5C199D00F1DE01 /* thread_annotations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_annotations.h; sourceTree = \"<group>\"; };\n\t\tBA34F93D1D5C199D00F1DE01 /* block.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = block.cc; sourceTree = \"<group>\"; };\n\t\tBA34F93E1D5C199D00F1DE01 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block.h; sourceTree = \"<group>\"; };\n\t\tBA34F93F1D5C199D00F1DE01 /* block_builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = block_builder.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9401D5C199D00F1DE01 /* block_builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block_builder.h; sourceTree = \"<group>\"; };\n\t\tBA34F9411D5C199D00F1DE01 /* filter_block.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_block.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9421D5C199D00F1DE01 /* filter_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter_block.h; sourceTree = \"<group>\"; };\n\t\tBA34F9441D5C199D00F1DE01 /* format.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9451D5C199D00F1DE01 /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = format.h; sourceTree = \"<group>\"; };\n\t\tBA34F9461D5C199D00F1DE01 /* iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iterator.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterator_wrapper.h; sourceTree = \"<group>\"; };\n\t\tBA34F9481D5C199D00F1DE01 /* merger.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = merger.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9491D5C199D00F1DE01 /* merger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = merger.h; sourceTree = \"<group>\"; };\n\t\tBA34F94A1D5C199D00F1DE01 /* table.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cc; sourceTree = \"<group>\"; };\n\t\tBA34F94B1D5C199D00F1DE01 /* table_builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table_builder.cc; sourceTree = \"<group>\"; };\n\t\tBA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = two_level_iterator.cc; sourceTree = \"<group>\"; };\n\t\tBA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = two_level_iterator.h; sourceTree = \"<group>\"; };\n\t\tBA34F9511D5C199D00F1DE01 /* arena.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = arena.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9521D5C199D00F1DE01 /* arena.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arena.h; sourceTree = \"<group>\"; };\n\t\tBA34F9541D5C199D00F1DE01 /* bloom.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bloom.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9561D5C199D00F1DE01 /* cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cache.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9581D5C199D00F1DE01 /* coding.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coding.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9591D5C199D00F1DE01 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = \"<group>\"; };\n\t\tBA34F95B1D5C199D00F1DE01 /* comparator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = comparator.cc; sourceTree = \"<group>\"; };\n\t\tBA34F95C1D5C199D00F1DE01 /* crc32c.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = crc32c.cc; sourceTree = \"<group>\"; };\n\t\tBA34F95D1D5C199D00F1DE01 /* crc32c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc32c.h; sourceTree = \"<group>\"; };\n\t\tBA34F95F1D5C199D00F1DE01 /* env.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9601D5C199D00F1DE01 /* env_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env_posix.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9621D5C199D00F1DE01 /* filter_policy.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_policy.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9631D5C199D00F1DE01 /* hash.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hash.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9641D5C199D00F1DE01 /* hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hash.h; sourceTree = \"<group>\"; };\n\t\tBA34F9661D5C199D00F1DE01 /* histogram.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = histogram.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9671D5C199D00F1DE01 /* histogram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = histogram.h; sourceTree = \"<group>\"; };\n\t\tBA34F9681D5C199D00F1DE01 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9691D5C199D00F1DE01 /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = \"<group>\"; };\n\t\tBA34F96A1D5C199D00F1DE01 /* mutexlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mutexlock.h; sourceTree = \"<group>\"; };\n\t\tBA34F96B1D5C199D00F1DE01 /* options.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = options.cc; sourceTree = \"<group>\"; };\n\t\tBA34F96C1D5C199D00F1DE01 /* posix_logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = posix_logger.h; sourceTree = \"<group>\"; };\n\t\tBA34F96D1D5C199D00F1DE01 /* random.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = random.h; sourceTree = \"<group>\"; };\n\t\tBA34F96E1D5C199D00F1DE01 /* status.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status.cc; sourceTree = \"<group>\"; };\n\t\tBA34F96F1D5C199D00F1DE01 /* testharness.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testharness.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9701D5C199D00F1DE01 /* testharness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testharness.h; sourceTree = \"<group>\"; };\n\t\tBA34F9711D5C199D00F1DE01 /* testutil.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testutil.cc; sourceTree = \"<group>\"; };\n\t\tBA34F9721D5C199D00F1DE01 /* testutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testutil.h; sourceTree = \"<group>\"; };\n\t\tBA34F9F11D5C1C9700F1DE01 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n\t\tBA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QDumpInterface.h; path = QedisCore/QDumpInterface.h; sourceTree = SOURCE_ROOT; };\n\t\tBA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QLeveldb.cc; path = QedisCore/QLeveldb.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA34F9F71D5C25E400F1DE01 /* QLeveldb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QLeveldb.h; path = QedisCore/QLeveldb.h; sourceTree = SOURCE_ROOT; };\n\t\tBA34F9FD1D5C269D00F1DE01 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = \"<group>\"; };\n\t\tBA3B0AD41ACBA9E1000D9399 /* QAOF.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QAOF.cc; path = QedisCore/QAOF.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA3B0AD51ACBA9E1000D9399 /* QAOF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QAOF.h; path = QedisCore/QAOF.h; sourceTree = SOURCE_ROOT; };\n\t\tBA4053911BA1AE3B00B93053 /* QReplication.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QReplication.cc; path = QedisCore/QReplication.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA4053921BA1AE3B00B93053 /* QReplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QReplication.h; path = QedisCore/QReplication.h; sourceTree = SOURCE_ROOT; };\n\t\tBA47763B1AB0773F00EDBB5D /* redisIntset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = redisIntset.c; path = QedisCore/redisIntset.c; sourceTree = SOURCE_ROOT; };\n\t\tBA47763C1AB0773F00EDBB5D /* redisIntset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = redisIntset.h; path = QedisCore/redisIntset.h; sourceTree = SOURCE_ROOT; };\n\t\tBA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.dylib\"; includeInIndex = 0; path = libqedismodule.dylib; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBA4CD21D1CEB54FF00F68F67 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = Modules/CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHashModule.cc; path = Modules/QHashModule.cc; sourceTree = \"<group>\"; };\n\t\tBA4CD21F1CEB54FF00F68F67 /* QHashModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHashModule.h; path = Modules/QHashModule.h; sourceTree = \"<group>\"; };\n\t\tBA4CD2201CEB54FF00F68F67 /* QListModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QListModule.cc; path = Modules/QListModule.cc; sourceTree = \"<group>\"; };\n\t\tBA4CD2211CEB54FF00F68F67 /* QListModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QListModule.h; path = Modules/QListModule.h; sourceTree = \"<group>\"; };\n\t\tBA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QModuleInit.cc; path = Modules/QModuleInit.cc; sourceTree = \"<group>\"; };\n\t\tBA4CD2231CEB54FF00F68F67 /* QModuleInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QModuleInit.h; path = Modules/QModuleInit.h; sourceTree = \"<group>\"; };\n\t\tBA4CD2241CEB54FF00F68F67 /* QSetModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSetModule.cc; path = Modules/QSetModule.cc; sourceTree = \"<group>\"; };\n\t\tBA4CD2251CEB54FF00F68F67 /* QSetModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSetModule.h; path = Modules/QSetModule.h; sourceTree = \"<group>\"; };\n\t\tBA4CD2301CEB61E100F68F67 /* QModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QModule.cc; path = QedisCore/QModule.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA4CD2311CEB61E100F68F67 /* QModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QModule.h; path = QedisCore/QModule.h; sourceTree = SOURCE_ROOT; };\n\t\tBA4CD2361CEBF10600F68F67 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = CMakeLists.txt; path = QedisCore/CMakeLists.txt; sourceTree = SOURCE_ROOT; };\n\t\tBA6C336A19EA77B200AC1800 /* QGlobRegex.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QGlobRegex.cc; path = QedisCore/QGlobRegex.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA6C336B19EA77B200AC1800 /* QGlobRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QGlobRegex.h; path = QedisCore/QGlobRegex.h; sourceTree = SOURCE_ROOT; };\n\t\tBA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QGlobRegex_unittest.cc; sourceTree = \"<group>\"; };\n\t\tBA6C337019EC0D9900AC1800 /* QPubsub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = QPubsub.h; path = QedisCore/QPubsub.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBA6E478C1F99056000EFBE6F /* QSlaveClient.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSlaveClient.cc; path = QedisCore/QSlaveClient.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA6E478D1F99056000EFBE6F /* QSlaveClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSlaveClient.h; path = QedisCore/QSlaveClient.h; sourceTree = SOURCE_ROOT; };\n\t\tBA6E47911F9905E600EFBE6F /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QSentinel/CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBA6E47921F9905E600EFBE6F /* QClusterClient.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QClusterClient.cc; path = QSentinel/QClusterClient.cc; sourceTree = \"<group>\"; };\n\t\tBA6E47931F9905E600EFBE6F /* QClusterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClusterClient.h; path = QSentinel/QClusterClient.h; sourceTree = \"<group>\"; };\n\t\tBA6E47941F9905E600EFBE6F /* QClusterInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClusterInterface.h; path = QSentinel/QClusterInterface.h; sourceTree = \"<group>\"; };\n\t\tBA6E47961F9905F500EFBE6F /* proto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proto.h; path = QSentinel/zookeeper/proto.h; sourceTree = \"<group>\"; };\n\t\tBA6E47971F9905F500EFBE6F /* recordio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = recordio.c; path = QSentinel/zookeeper/recordio.c; sourceTree = \"<group>\"; };\n\t\tBA6E47981F9905F500EFBE6F /* recordio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = recordio.h; path = QSentinel/zookeeper/recordio.h; sourceTree = \"<group>\"; };\n\t\tBA6E47991F9905F500EFBE6F /* zookeeper_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zookeeper_version.h; path = QSentinel/zookeeper/zookeeper_version.h; sourceTree = \"<group>\"; };\n\t\tBA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zookeeper.jute.c; path = QSentinel/zookeeper/zookeeper.jute.c; sourceTree = \"<group>\"; };\n\t\tBA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zookeeper.jute.h; path = QSentinel/zookeeper/zookeeper.jute.h; sourceTree = \"<group>\"; };\n\t\tBA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZookeeperConn.cc; path = QSentinel/zookeeper/ZookeeperConn.cc; sourceTree = \"<group>\"; };\n\t\tBA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZookeeperConn.h; path = QSentinel/zookeeper/ZookeeperConn.h; sourceTree = \"<group>\"; };\n\t\tBA6E47A51F99064D00EFBE6F /* libqcluster.dylib */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.dylib\"; includeInIndex = 0; path = libqcluster.dylib; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBA98F83B1B01B16600ABCBD9 /* QConfig.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QConfig.cc; path = QedisCore/QConfig.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA98F83C1B01B16600ABCBD9 /* QConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QConfig.h; path = QedisCore/QConfig.h; sourceTree = SOURCE_ROOT; };\n\t\tBA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSlowLog.cc; path = QedisCore/QSlowLog.cc; sourceTree = SOURCE_ROOT; };\n\t\tBA98F8401B1B539600ABCBD9 /* QSlowLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSlowLog.h; path = QedisCore/QSlowLog.h; sourceTree = SOURCE_ROOT; };\n\t\tBA99A7591CF19ED20051C43C /* qedis.conf */ = {isa = PBXFileReference; lastKnownFileType = text; path = qedis.conf; sourceTree = \"<group>\"; };\n\t\tBA99CC521C52852C00BB88F7 /* Qedis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Qedis.h; path = QedisSvr/Qedis.h; sourceTree = \"<group>\"; };\n\t\tBAB2265C2007B7B7005241F6 /* QMigration.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QMigration.cc; path = QedisCore/QMigration.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAB2265D2007B7B7005241F6 /* QMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QMigration.h; path = QedisCore/QMigration.h; sourceTree = SOURCE_ROOT; };\n\t\tBABEBED519C4474900010636 /* Qedis */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = Qedis; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBAC9098D1B9D30DB002003C2 /* CMakeCommon */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeCommon; sourceTree = \"<group>\"; };\n\t\tBAC9098E1B9D30DB002003C2 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBAD0C5D919E79FF7005B3784 /* libqedislib.dylib */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libqedislib.dylib; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBAD0C5E319E7A02C005B3784 /* QClient.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = QClient.cc; path = QedisCore/QClient.cc; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAD0C5E419E7A02C005B3784 /* QClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClient.h; path = QedisCore/QClient.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5E519E7A02C005B3784 /* QCommand.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = QCommand.cc; path = QedisCore/QCommand.cc; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAD0C5E619E7A02C005B3784 /* QCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = QCommand.h; path = QedisCore/QCommand.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAD0C5E719E7A02C005B3784 /* QCommon.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QCommon.cc; path = QedisCore/QCommon.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5E819E7A02C005B3784 /* QCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = QCommon.h; path = QedisCore/QCommon.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAD0C5EA19E7A02C005B3784 /* QHash.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHash.cc; path = QedisCore/QHash.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5EB19E7A02C005B3784 /* QHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHash.h; path = QedisCore/QHash.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5EC19E7A02C005B3784 /* QKeyCommand.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = QKeyCommand.cc; path = QedisCore/QKeyCommand.cc; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAD0C5ED19E7A02C005B3784 /* QList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QList.cc; path = QedisCore/QList.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5EE19E7A02C005B3784 /* QList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QList.h; path = QedisCore/QList.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5EF19E7A02C005B3784 /* QPubsub.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QPubsub.cc; path = QedisCore/QPubsub.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F019E7A02C005B3784 /* QServerCommand.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QServerCommand.cc; path = QedisCore/QServerCommand.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F119E7A02C005B3784 /* QSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSet.cc; path = QedisCore/QSet.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F219E7A02C005B3784 /* QSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSet.h; path = QedisCore/QSet.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F319E7A02C005B3784 /* QSortedSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSortedSet.cc; path = QedisCore/QSortedSet.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F419E7A02C005B3784 /* QSortedSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSortedSet.h; path = QedisCore/QSortedSet.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5F719E7A02C005B3784 /* QStore.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = QStore.cc; path = QedisCore/QStore.cc; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAD0C5F819E7A02C005B3784 /* QStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = QStore.h; path = QedisCore/QStore.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAD0C5F919E7A02C005B3784 /* QString.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = QString.cc; path = QedisCore/QString.cc; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAD0C5FA19E7A02C005B3784 /* QString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = QString.h; path = QedisCore/QString.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAD0C5FB19E7A02C005B3784 /* QHelper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHelper.cc; path = QedisCore/QHelper.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C5FC19E7A02C005B3784 /* QHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHelper.h; path = QedisCore/QHelper.h; sourceTree = SOURCE_ROOT; };\n\t\tBAD0C61919E7A075005B3784 /* Qedis.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Qedis.cc; path = QedisSvr/Qedis.cc; sourceTree = \"<group>\"; };\n\t\tBAD0C61C19E7A09F005B3784 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QedisSvr/CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBAD0C61D19E7A18B005B3784 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBAD0C62919E7A9D8005B3784 /* UnitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UnitTest.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBADC7D7119D6BC700036858E /* UnitTest */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = UnitTest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBADC7D7A19D6BC960036858E /* UnitTest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = UnitTest.cc; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBADD49B31CCA68BA0093257E /* QProtoParser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QProtoParser.cc; path = QedisCore/QProtoParser.cc; sourceTree = SOURCE_ROOT; };\n\t\tBADD49B41CCA68BA0093257E /* QProtoParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QProtoParser.h; path = QedisCore/QProtoParser.h; sourceTree = SOURCE_ROOT; };\n\t\tBAFBDDB51CECAB2400E12FBA /* QedisLogo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QedisLogo.h; path = QedisSvr/QedisLogo.h; sourceTree = \"<group>\"; };\n\t\tBAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libqbaselib.dylib; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBAFC840219C44AFD00C18B22 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Buffer.h; path = QBase/Buffer.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAFC840419C44AFD00C18B22 /* ClientSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClientSocket.cc; path = QBase/ClientSocket.cc; sourceTree = \"<group>\"; };\n\t\tBAFC840519C44AFD00C18B22 /* ClientSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClientSocket.h; path = QBase/ClientSocket.h; sourceTree = \"<group>\"; };\n\t\tBAFC840619C44AFD00C18B22 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QBase/CMakeLists.txt; sourceTree = \"<group>\"; };\n\t\tBAFC840719C44AFD00C18B22 /* ConfigParser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = ConfigParser.cc; path = QBase/ConfigParser.cc; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAFC840819C44AFD00C18B22 /* ConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = ConfigParser.h; path = QBase/ConfigParser.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAFC840919C44AFD00C18B22 /* EPoller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = EPoller.cc; path = QBase/EPoller.cc; sourceTree = \"<group>\"; };\n\t\tBAFC840A19C44AFD00C18B22 /* EPoller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EPoller.h; path = QBase/EPoller.h; sourceTree = \"<group>\"; };\n\t\tBAFC840B19C44AFD00C18B22 /* Kqueue.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Kqueue.cc; path = QBase/Kqueue.cc; sourceTree = \"<group>\"; };\n\t\tBAFC840C19C44AFD00C18B22 /* Kqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Kqueue.h; path = QBase/Kqueue.h; sourceTree = \"<group>\"; };\n\t\tBAFC840D19C44AFD00C18B22 /* ListenSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ListenSocket.cc; path = QBase/ListenSocket.cc; sourceTree = \"<group>\"; };\n\t\tBAFC840E19C44AFD00C18B22 /* ListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ListenSocket.h; path = QBase/ListenSocket.h; sourceTree = \"<group>\"; };\n\t\tBAFC841019C44AFD00C18B22 /* Logger.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Logger.cc; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAFC841119C44AFD00C18B22 /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Logger.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetThreadPool.cc; path = QBase/NetThreadPool.cc; sourceTree = \"<group>\"; };\n\t\tBAFC842B19C44AFD00C18B22 /* NetThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetThreadPool.h; path = QBase/NetThreadPool.h; sourceTree = \"<group>\"; };\n\t\tBAFC842C19C44AFD00C18B22 /* Poller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Poller.h; path = QBase/Poller.h; sourceTree = \"<group>\"; };\n\t\tBAFC842D19C44AFD00C18B22 /* Server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Server.cc; path = QBase/Server.cc; sourceTree = \"<group>\"; };\n\t\tBAFC842E19C44AFD00C18B22 /* Server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Server.h; path = QBase/Server.h; sourceTree = \"<group>\"; };\n\t\tBAFC846019C44AFD00C18B22 /* Socket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = Socket.cc; path = QBase/Socket.cc; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };\n\t\tBAFC846119C44AFD00C18B22 /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Socket.h; path = QBase/Socket.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAFC846219C44AFD00C18B22 /* StreamSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamSocket.cc; path = QBase/StreamSocket.cc; sourceTree = \"<group>\"; };\n\t\tBAFC846319C44AFD00C18B22 /* StreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamSocket.h; path = QBase/StreamSocket.h; sourceTree = \"<group>\"; };\n\t\tBAFC846419C44AFD00C18B22 /* TaskManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TaskManager.cc; path = QBase/TaskManager.cc; sourceTree = \"<group>\"; };\n\t\tBAFC846519C44AFD00C18B22 /* TaskManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TaskManager.h; path = QBase/TaskManager.h; sourceTree = \"<group>\"; };\n\t\tBAFC846C19C44AFD00C18B22 /* ThreadPool.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadPool.cc; sourceTree = \"<group>\"; };\n\t\tBAFC846D19C44AFD00C18B22 /* ThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ThreadPool.h; sourceTree = \"<group>\"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };\n\t\tBAFC846E19C44AFD00C18B22 /* Timer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Timer.cc; path = QBase/Timer.cc; sourceTree = \"<group>\"; };\n\t\tBAFC846F19C44AFD00C18B22 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Timer.h; path = QBase/Timer.h; sourceTree = \"<group>\"; };\n\t\tBAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnboundedBuffer.cc; path = QBase/UnboundedBuffer.cc; sourceTree = \"<group>\"; };\n\t\tBAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnboundedBuffer.h; path = QBase/UnboundedBuffer.h; sourceTree = \"<group>\"; };\n\t\tBAFFCB511A8F09CA00C47F92 /* QDB.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QDB.cc; path = QedisCore/QDB.cc; sourceTree = SOURCE_ROOT; };\n\t\tBAFFCB521A8F09CA00C47F92 /* QDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QDB.h; path = QedisCore/QDB.h; sourceTree = SOURCE_ROOT; };\n\t\tBAFFCB561A9244B200C47F92 /* lzf_c.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzf_c.c; path = QBase/lzf/lzf_c.c; sourceTree = \"<group>\"; };\n\t\tBAFFCB571A9244B200C47F92 /* lzf_d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzf_d.c; path = QBase/lzf/lzf_d.c; sourceTree = \"<group>\"; };\n\t\tBAFFCB581A9244B200C47F92 /* lzf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lzf.h; path = QBase/lzf/lzf.h; sourceTree = \"<group>\"; };\n\t\tBAFFCB591A9244B200C47F92 /* lzfP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lzfP.h; path = QBase/lzf/lzfP.h; sourceTree = \"<group>\"; };\n\t\tBAFFCB5E1A9423DC00C47F92 /* crc64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = crc64.c; path = QedisCore/crc64.c; sourceTree = SOURCE_ROOT; };\n\t\tBAFFCB601A9EA0AC00C47F92 /* redisZipList.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = redisZipList.c; path = QedisCore/redisZipList.c; sourceTree = SOURCE_ROOT; };\n\t\tBAFFCB611A9EA0AC00C47F92 /* redisZipList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = redisZipList.h; path = QedisCore/redisZipList.h; sourceTree = SOURCE_ROOT; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tBA34F6001D5C151700F1DE01 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA4CD2161CEB54F000F68F67 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA6E47A21F99064D00EFBE6F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBABEBED219C4474900010636 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA34F9FC1D5C266200F1DE01 /* libleveldb.dylib in Frameworks */,\n\t\t\t\tBA34F9FB1D5C265D00F1DE01 /* libqbaselib.dylib in Frameworks */,\n\t\t\t\tBA34F9F31D5C243800F1DE01 /* libqedislib.dylib in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAD0C5D619E79FF7005B3784 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA34F9F41D5C244E00F1DE01 /* libqbaselib.dylib in Frameworks */,\n\t\t\t\tBA34F9F21D5C221700F1DE01 /* libleveldb.dylib in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBADC7D6E19D6BC700036858E /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBAD0C62519E7A8DA005B3784 /* libqedislib.dylib in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAFC83FB19C44AE800C18B22 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tBA34F8DF1D5C198B00F1DE01 /* leveldb */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F8E11D5C199D00F1DE01 /* db */,\n\t\t\t\tBA34F9181D5C199D00F1DE01 /* helpers */,\n\t\t\t\tBA34F91D1D5C199D00F1DE01 /* include */,\n\t\t\t\tBA34F9311D5C199D00F1DE01 /* port */,\n\t\t\t\tBA34F93C1D5C199D00F1DE01 /* table */,\n\t\t\t\tBA34F9501D5C199D00F1DE01 /* util */,\n\t\t\t);\n\t\t\tname = leveldb;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F8E11D5C199D00F1DE01 /* db */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F8E31D5C199D00F1DE01 /* builder.cc */,\n\t\t\t\tBA34F8E41D5C199D00F1DE01 /* builder.h */,\n\t\t\t\tBA34F8E51D5C199D00F1DE01 /* c.cc */,\n\t\t\t\tBA34F8E81D5C199D00F1DE01 /* db_bench.cc */,\n\t\t\t\tBA34F8E91D5C199D00F1DE01 /* db_impl.cc */,\n\t\t\t\tBA34F8EA1D5C199D00F1DE01 /* db_impl.h */,\n\t\t\t\tBA34F8EB1D5C199D00F1DE01 /* db_iter.cc */,\n\t\t\t\tBA34F8EC1D5C199D00F1DE01 /* db_iter.h */,\n\t\t\t\tBA34F8EE1D5C199D00F1DE01 /* dbformat.cc */,\n\t\t\t\tBA34F8EF1D5C199D00F1DE01 /* dbformat.h */,\n\t\t\t\tBA34F8F11D5C199D00F1DE01 /* dumpfile.cc */,\n\t\t\t\tBA34F8F31D5C199D00F1DE01 /* filename.cc */,\n\t\t\t\tBA34F8F41D5C199D00F1DE01 /* filename.h */,\n\t\t\t\tBA34F8F71D5C199D00F1DE01 /* log_format.h */,\n\t\t\t\tBA34F8F81D5C199D00F1DE01 /* log_reader.cc */,\n\t\t\t\tBA34F8F91D5C199D00F1DE01 /* log_reader.h */,\n\t\t\t\tBA34F8FB1D5C199D00F1DE01 /* log_writer.cc */,\n\t\t\t\tBA34F8FC1D5C199D00F1DE01 /* log_writer.h */,\n\t\t\t\tBA34F8FD1D5C199D00F1DE01 /* memtable.cc */,\n\t\t\t\tBA34F8FE1D5C199D00F1DE01 /* memtable.h */,\n\t\t\t\tBA34F8FF1D5C199D00F1DE01 /* repair.cc */,\n\t\t\t\tBA34F9001D5C199D00F1DE01 /* skiplist.h */,\n\t\t\t\tBA34F9021D5C199D00F1DE01 /* snapshot.h */,\n\t\t\t\tBA34F9031D5C199D00F1DE01 /* table_cache.cc */,\n\t\t\t\tBA34F9041D5C199D00F1DE01 /* table_cache.h */,\n\t\t\t\tBA34F9051D5C199D00F1DE01 /* version_edit.cc */,\n\t\t\t\tBA34F9061D5C199D00F1DE01 /* version_edit.h */,\n\t\t\t\tBA34F9081D5C199D00F1DE01 /* version_set.cc */,\n\t\t\t\tBA34F9091D5C199D00F1DE01 /* version_set.h */,\n\t\t\t\tBA34F90B1D5C199D00F1DE01 /* write_batch.cc */,\n\t\t\t\tBA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */,\n\t\t\t);\n\t\t\tname = db;\n\t\t\tpath = leveldb/db;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F9181D5C199D00F1DE01 /* helpers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F9191D5C199D00F1DE01 /* memenv */,\n\t\t\t);\n\t\t\tname = helpers;\n\t\t\tpath = leveldb/helpers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F9191D5C199D00F1DE01 /* memenv */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F91A1D5C199D00F1DE01 /* memenv.cc */,\n\t\t\t\tBA34F91B1D5C199D00F1DE01 /* memenv.h */,\n\t\t\t);\n\t\t\tpath = memenv;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F91D1D5C199D00F1DE01 /* include */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F91E1D5C199D00F1DE01 /* leveldb */,\n\t\t\t);\n\t\t\tname = include;\n\t\t\tpath = leveldb/include;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F91E1D5C199D00F1DE01 /* leveldb */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F91F1D5C199D00F1DE01 /* c.h */,\n\t\t\t\tBA34F9201D5C199D00F1DE01 /* cache.h */,\n\t\t\t\tBA34F9211D5C199D00F1DE01 /* comparator.h */,\n\t\t\t\tBA34F9221D5C199D00F1DE01 /* db.h */,\n\t\t\t\tBA34F9231D5C199D00F1DE01 /* dumpfile.h */,\n\t\t\t\tBA34F9241D5C199D00F1DE01 /* env.h */,\n\t\t\t\tBA34F9251D5C199D00F1DE01 /* filter_policy.h */,\n\t\t\t\tBA34F9261D5C199D00F1DE01 /* iterator.h */,\n\t\t\t\tBA34F9271D5C199D00F1DE01 /* options.h */,\n\t\t\t\tBA34F9281D5C199D00F1DE01 /* slice.h */,\n\t\t\t\tBA34F9291D5C199D00F1DE01 /* status.h */,\n\t\t\t\tBA34F92A1D5C199D00F1DE01 /* table.h */,\n\t\t\t\tBA34F92B1D5C199D00F1DE01 /* table_builder.h */,\n\t\t\t\tBA34F92C1D5C199D00F1DE01 /* write_batch.h */,\n\t\t\t);\n\t\t\tpath = leveldb;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F9311D5C199D00F1DE01 /* port */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F9321D5C199D00F1DE01 /* atomic_pointer.h */,\n\t\t\t\tBA34F9331D5C199D00F1DE01 /* port.h */,\n\t\t\t\tBA34F9341D5C199D00F1DE01 /* port_example.h */,\n\t\t\t\tBA34F9351D5C199D00F1DE01 /* port_posix.cc */,\n\t\t\t\tBA34F9361D5C199D00F1DE01 /* port_posix.h */,\n\t\t\t\tBA34F9381D5C199D00F1DE01 /* thread_annotations.h */,\n\t\t\t);\n\t\t\tname = port;\n\t\t\tpath = leveldb/port;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F93C1D5C199D00F1DE01 /* table */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F93D1D5C199D00F1DE01 /* block.cc */,\n\t\t\t\tBA34F93E1D5C199D00F1DE01 /* block.h */,\n\t\t\t\tBA34F93F1D5C199D00F1DE01 /* block_builder.cc */,\n\t\t\t\tBA34F9401D5C199D00F1DE01 /* block_builder.h */,\n\t\t\t\tBA34F9411D5C199D00F1DE01 /* filter_block.cc */,\n\t\t\t\tBA34F9421D5C199D00F1DE01 /* filter_block.h */,\n\t\t\t\tBA34F9441D5C199D00F1DE01 /* format.cc */,\n\t\t\t\tBA34F9451D5C199D00F1DE01 /* format.h */,\n\t\t\t\tBA34F9461D5C199D00F1DE01 /* iterator.cc */,\n\t\t\t\tBA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */,\n\t\t\t\tBA34F9481D5C199D00F1DE01 /* merger.cc */,\n\t\t\t\tBA34F9491D5C199D00F1DE01 /* merger.h */,\n\t\t\t\tBA34F94A1D5C199D00F1DE01 /* table.cc */,\n\t\t\t\tBA34F94B1D5C199D00F1DE01 /* table_builder.cc */,\n\t\t\t\tBA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */,\n\t\t\t\tBA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */,\n\t\t\t);\n\t\t\tname = table;\n\t\t\tpath = leveldb/table;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA34F9501D5C199D00F1DE01 /* util */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F9511D5C199D00F1DE01 /* arena.cc */,\n\t\t\t\tBA34F9521D5C199D00F1DE01 /* arena.h */,\n\t\t\t\tBA34F9541D5C199D00F1DE01 /* bloom.cc */,\n\t\t\t\tBA34F9561D5C199D00F1DE01 /* cache.cc */,\n\t\t\t\tBA34F9581D5C199D00F1DE01 /* coding.cc */,\n\t\t\t\tBA34F9591D5C199D00F1DE01 /* coding.h */,\n\t\t\t\tBA34F95B1D5C199D00F1DE01 /* comparator.cc */,\n\t\t\t\tBA34F95C1D5C199D00F1DE01 /* crc32c.cc */,\n\t\t\t\tBA34F95D1D5C199D00F1DE01 /* crc32c.h */,\n\t\t\t\tBA34F95F1D5C199D00F1DE01 /* env.cc */,\n\t\t\t\tBA34F9601D5C199D00F1DE01 /* env_posix.cc */,\n\t\t\t\tBA34F9621D5C199D00F1DE01 /* filter_policy.cc */,\n\t\t\t\tBA34F9631D5C199D00F1DE01 /* hash.cc */,\n\t\t\t\tBA34F9641D5C199D00F1DE01 /* hash.h */,\n\t\t\t\tBA34F9661D5C199D00F1DE01 /* histogram.cc */,\n\t\t\t\tBA34F9671D5C199D00F1DE01 /* histogram.h */,\n\t\t\t\tBA34F9681D5C199D00F1DE01 /* logging.cc */,\n\t\t\t\tBA34F9691D5C199D00F1DE01 /* logging.h */,\n\t\t\t\tBA34F96A1D5C199D00F1DE01 /* mutexlock.h */,\n\t\t\t\tBA34F96B1D5C199D00F1DE01 /* options.cc */,\n\t\t\t\tBA34F96C1D5C199D00F1DE01 /* posix_logger.h */,\n\t\t\t\tBA34F96D1D5C199D00F1DE01 /* random.h */,\n\t\t\t\tBA34F96E1D5C199D00F1DE01 /* status.cc */,\n\t\t\t\tBA34F96F1D5C199D00F1DE01 /* testharness.cc */,\n\t\t\t\tBA34F9701D5C199D00F1DE01 /* testharness.h */,\n\t\t\t\tBA34F9711D5C199D00F1DE01 /* testutil.cc */,\n\t\t\t\tBA34F9721D5C199D00F1DE01 /* testutil.h */,\n\t\t\t);\n\t\t\tname = util;\n\t\t\tpath = leveldb/util;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA4CD2141CEB54BC00F68F67 /* Modules */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA4CD21D1CEB54FF00F68F67 /* CMakeLists.txt */,\n\t\t\t\tBA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */,\n\t\t\t\tBA4CD21F1CEB54FF00F68F67 /* QHashModule.h */,\n\t\t\t\tBA4CD2201CEB54FF00F68F67 /* QListModule.cc */,\n\t\t\t\tBA4CD2211CEB54FF00F68F67 /* QListModule.h */,\n\t\t\t\tBA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */,\n\t\t\t\tBA4CD2231CEB54FF00F68F67 /* QModuleInit.h */,\n\t\t\t\tBA4CD2241CEB54FF00F68F67 /* QSetModule.cc */,\n\t\t\t\tBA4CD2251CEB54FF00F68F67 /* QSetModule.h */,\n\t\t\t);\n\t\t\tname = Modules;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA6E478F1F9905C800EFBE6F /* QSentinel */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA6E47911F9905E600EFBE6F /* CMakeLists.txt */,\n\t\t\t\tBA6E47921F9905E600EFBE6F /* QClusterClient.cc */,\n\t\t\t\tBA6E47931F9905E600EFBE6F /* QClusterClient.h */,\n\t\t\t\tBA6E47941F9905E600EFBE6F /* QClusterInterface.h */,\n\t\t\t\tBA6E47901F9905D400EFBE6F /* zookeeper */,\n\t\t\t);\n\t\t\tname = QSentinel;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBA6E47901F9905D400EFBE6F /* zookeeper */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA6E47961F9905F500EFBE6F /* proto.h */,\n\t\t\t\tBA6E47971F9905F500EFBE6F /* recordio.c */,\n\t\t\t\tBA6E47981F9905F500EFBE6F /* recordio.h */,\n\t\t\t\tBA6E47991F9905F500EFBE6F /* zookeeper_version.h */,\n\t\t\t\tBA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */,\n\t\t\t\tBA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */,\n\t\t\t\tBA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */,\n\t\t\t\tBA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */,\n\t\t\t);\n\t\t\tname = zookeeper;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBAA158B219C44A0F007713F9 /* QBase */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA2C0C7A1CDC1DC800BDF050 /* Delegate.h */,\n\t\t\t\tBAFFCB551A92449F00C47F92 /* lzf */,\n\t\t\t\tBA12507F1A7338CD00A0532D /* AsyncBuffer.cc */,\n\t\t\t\tBA1250801A7338CD00A0532D /* AsyncBuffer.h */,\n\t\t\t\tBAFC840219C44AFD00C18B22 /* Buffer.h */,\n\t\t\t\tBAFC840419C44AFD00C18B22 /* ClientSocket.cc */,\n\t\t\t\tBAFC840519C44AFD00C18B22 /* ClientSocket.h */,\n\t\t\t\tBAFC840619C44AFD00C18B22 /* CMakeLists.txt */,\n\t\t\t\tBAFC840719C44AFD00C18B22 /* ConfigParser.cc */,\n\t\t\t\tBAFC840819C44AFD00C18B22 /* ConfigParser.h */,\n\t\t\t\tBAFC840919C44AFD00C18B22 /* EPoller.cc */,\n\t\t\t\tBAFC840A19C44AFD00C18B22 /* EPoller.h */,\n\t\t\t\tBAFC840B19C44AFD00C18B22 /* Kqueue.cc */,\n\t\t\t\tBAFC840C19C44AFD00C18B22 /* Kqueue.h */,\n\t\t\t\tBAFC840E19C44AFD00C18B22 /* ListenSocket.h */,\n\t\t\t\tBAFC840F19C44AFD00C18B22 /* Log */,\n\t\t\t\tBAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */,\n\t\t\t\tBAFC842B19C44AFD00C18B22 /* NetThreadPool.h */,\n\t\t\t\tBAFC842C19C44AFD00C18B22 /* Poller.h */,\n\t\t\t\tBAFC842D19C44AFD00C18B22 /* Server.cc */,\n\t\t\t\tBAFC842E19C44AFD00C18B22 /* Server.h */,\n\t\t\t\tBAFC846019C44AFD00C18B22 /* Socket.cc */,\n\t\t\t\tBAFC846119C44AFD00C18B22 /* Socket.h */,\n\t\t\t\tBAFC846219C44AFD00C18B22 /* StreamSocket.cc */,\n\t\t\t\tBAFC840D19C44AFD00C18B22 /* ListenSocket.cc */,\n\t\t\t\tBAFC846319C44AFD00C18B22 /* StreamSocket.h */,\n\t\t\t\tBAFC846419C44AFD00C18B22 /* TaskManager.cc */,\n\t\t\t\tBAFC846519C44AFD00C18B22 /* TaskManager.h */,\n\t\t\t\tBAFC846619C44AFD00C18B22 /* Threads */,\n\t\t\t\tBAFC846E19C44AFD00C18B22 /* Timer.cc */,\n\t\t\t\tBAFC846F19C44AFD00C18B22 /* Timer.h */,\n\t\t\t\tBAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */,\n\t\t\t\tBAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */,\n\t\t\t);\n\t\t\tname = QBase;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBABEBECC19C4474900010636 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA34F9FD1D5C269D00F1DE01 /* Makefile */,\n\t\t\t\tBA99A7591CF19ED20051C43C /* qedis.conf */,\n\t\t\t\tBA34F9F11D5C1C9700F1DE01 /* README.md */,\n\t\t\t\tBAC9098D1B9D30DB002003C2 /* CMakeCommon */,\n\t\t\t\tBAC9098E1B9D30DB002003C2 /* CMakeLists.txt */,\n\t\t\t\tBA34F8DF1D5C198B00F1DE01 /* leveldb */,\n\t\t\t\tBAD0C61819E7A059005B3784 /* QedisSvr */,\n\t\t\t\tBAA158B219C44A0F007713F9 /* QBase */,\n\t\t\t\tBABEBED719C4474900010636 /* QedisCore */,\n\t\t\t\tBA6E478F1F9905C800EFBE6F /* QSentinel */,\n\t\t\t\tBA4CD2141CEB54BC00F68F67 /* Modules */,\n\t\t\t\tBADC7D7219D6BC700036858E /* UnitTest */,\n\t\t\t\tBABEBED619C4474900010636 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBABEBED619C4474900010636 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBABEBED519C4474900010636 /* Qedis */,\n\t\t\t\tBAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */,\n\t\t\t\tBADC7D7119D6BC700036858E /* UnitTest */,\n\t\t\t\tBAD0C5D919E79FF7005B3784 /* libqedislib.dylib */,\n\t\t\t\tBA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */,\n\t\t\t\tBA34F6031D5C151700F1DE01 /* libleveldb.dylib */,\n\t\t\t\tBA6E47A51F99064D00EFBE6F /* libqcluster.dylib */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBABEBED719C4474900010636 /* QedisCore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBAB2265C2007B7B7005241F6 /* QMigration.cc */,\n\t\t\t\tBAB2265D2007B7B7005241F6 /* QMigration.h */,\n\t\t\t\tBA6E478C1F99056000EFBE6F /* QSlaveClient.cc */,\n\t\t\t\tBA6E478D1F99056000EFBE6F /* QSlaveClient.h */,\n\t\t\t\tBA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */,\n\t\t\t\tBA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */,\n\t\t\t\tBA34F9F71D5C25E400F1DE01 /* QLeveldb.h */,\n\t\t\t\tBA4CD2361CEBF10600F68F67 /* CMakeLists.txt */,\n\t\t\t\tBA4CD2301CEB61E100F68F67 /* QModule.cc */,\n\t\t\t\tBA4CD2311CEB61E100F68F67 /* QModule.h */,\n\t\t\t\tBADD49B31CCA68BA0093257E /* QProtoParser.cc */,\n\t\t\t\tBADD49B41CCA68BA0093257E /* QProtoParser.h */,\n\t\t\t\tBA4053911BA1AE3B00B93053 /* QReplication.cc */,\n\t\t\t\tBA4053921BA1AE3B00B93053 /* QReplication.h */,\n\t\t\t\tBA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */,\n\t\t\t\tBA98F8401B1B539600ABCBD9 /* QSlowLog.h */,\n\t\t\t\tBA98F83B1B01B16600ABCBD9 /* QConfig.cc */,\n\t\t\t\tBA98F83C1B01B16600ABCBD9 /* QConfig.h */,\n\t\t\t\tBA3B0AD41ACBA9E1000D9399 /* QAOF.cc */,\n\t\t\t\tBA3B0AD51ACBA9E1000D9399 /* QAOF.h */,\n\t\t\t\tBA47763B1AB0773F00EDBB5D /* redisIntset.c */,\n\t\t\t\tBA47763C1AB0773F00EDBB5D /* redisIntset.h */,\n\t\t\t\tBAFFCB601A9EA0AC00C47F92 /* redisZipList.c */,\n\t\t\t\tBAFFCB611A9EA0AC00C47F92 /* redisZipList.h */,\n\t\t\t\tBAFFCB5E1A9423DC00C47F92 /* crc64.c */,\n\t\t\t\tBAFFCB511A8F09CA00C47F92 /* QDB.cc */,\n\t\t\t\tBAFFCB521A8F09CA00C47F92 /* QDB.h */,\n\t\t\t\tBA29C44C19FB2CEE00796E3A /* QMulti.cc */,\n\t\t\t\tBA29C44D19FB2CEE00796E3A /* QMulti.h */,\n\t\t\t\tBA6C336A19EA77B200AC1800 /* QGlobRegex.cc */,\n\t\t\t\tBA6C336B19EA77B200AC1800 /* QGlobRegex.h */,\n\t\t\t\tBAD0C5E319E7A02C005B3784 /* QClient.cc */,\n\t\t\t\tBAD0C5E419E7A02C005B3784 /* QClient.h */,\n\t\t\t\tBAD0C5E519E7A02C005B3784 /* QCommand.cc */,\n\t\t\t\tBAD0C5E619E7A02C005B3784 /* QCommand.h */,\n\t\t\t\tBAD0C5E719E7A02C005B3784 /* QCommon.cc */,\n\t\t\t\tBAD0C5E819E7A02C005B3784 /* QCommon.h */,\n\t\t\t\tBAD0C5EA19E7A02C005B3784 /* QHash.cc */,\n\t\t\t\tBAD0C5EB19E7A02C005B3784 /* QHash.h */,\n\t\t\t\tBAD0C5EC19E7A02C005B3784 /* QKeyCommand.cc */,\n\t\t\t\tBAD0C5ED19E7A02C005B3784 /* QList.cc */,\n\t\t\t\tBAD0C5EE19E7A02C005B3784 /* QList.h */,\n\t\t\t\tBA6C337019EC0D9900AC1800 /* QPubsub.h */,\n\t\t\t\tBAD0C5EF19E7A02C005B3784 /* QPubsub.cc */,\n\t\t\t\tBAD0C5F019E7A02C005B3784 /* QServerCommand.cc */,\n\t\t\t\tBAD0C5F119E7A02C005B3784 /* QSet.cc */,\n\t\t\t\tBAD0C5F219E7A02C005B3784 /* QSet.h */,\n\t\t\t\tBAD0C5F319E7A02C005B3784 /* QSortedSet.cc */,\n\t\t\t\tBAD0C5F419E7A02C005B3784 /* QSortedSet.h */,\n\t\t\t\tBAD0C5F719E7A02C005B3784 /* QStore.cc */,\n\t\t\t\tBAD0C5F819E7A02C005B3784 /* QStore.h */,\n\t\t\t\tBAD0C5F919E7A02C005B3784 /* QString.cc */,\n\t\t\t\tBAD0C5FA19E7A02C005B3784 /* QString.h */,\n\t\t\t\tBAD0C5FB19E7A02C005B3784 /* QHelper.cc */,\n\t\t\t\tBAD0C5FC19E7A02C005B3784 /* QHelper.h */,\n\t\t\t);\n\t\t\tname = QedisCore;\n\t\t\tpath = Qedis;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBAD0C61819E7A059005B3784 /* QedisSvr */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBAFBDDB51CECAB2400E12FBA /* QedisLogo.h */,\n\t\t\t\tBA99CC521C52852C00BB88F7 /* Qedis.h */,\n\t\t\t\tBAD0C61C19E7A09F005B3784 /* CMakeLists.txt */,\n\t\t\t\tBAD0C61919E7A075005B3784 /* Qedis.cc */,\n\t\t\t);\n\t\t\tname = QedisSvr;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBADC7D7219D6BC700036858E /* UnitTest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */,\n\t\t\t\tBAD0C62919E7A9D8005B3784 /* UnitTest.h */,\n\t\t\t\tBAD0C61D19E7A18B005B3784 /* CMakeLists.txt */,\n\t\t\t\tBADC7D7A19D6BC960036858E /* UnitTest.cc */,\n\t\t\t);\n\t\t\tpath = UnitTest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBAFC840F19C44AFD00C18B22 /* Log */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA12506F1A6D43D500A0532D /* MemoryFile.cc */,\n\t\t\t\tBA1250701A6D43D500A0532D /* MemoryFile.h */,\n\t\t\t\tBAFC841019C44AFD00C18B22 /* Logger.cc */,\n\t\t\t\tBAFC841119C44AFD00C18B22 /* Logger.h */,\n\t\t\t);\n\t\t\tname = Log;\n\t\t\tpath = QBase/Log;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBAFC846619C44AFD00C18B22 /* Threads */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBAFC846C19C44AFD00C18B22 /* ThreadPool.cc */,\n\t\t\t\tBAFC846D19C44AFD00C18B22 /* ThreadPool.h */,\n\t\t\t);\n\t\t\tname = Threads;\n\t\t\tpath = QBase/Threads;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBAFFCB551A92449F00C47F92 /* lzf */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBAFFCB561A9244B200C47F92 /* lzf_c.c */,\n\t\t\t\tBAFFCB571A9244B200C47F92 /* lzf_d.c */,\n\t\t\t\tBAFFCB581A9244B200C47F92 /* lzf.h */,\n\t\t\t\tBAFFCB591A9244B200C47F92 /* lzfP.h */,\n\t\t\t);\n\t\t\tname = lzf;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tBA34F6011D5C151700F1DE01 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA34F9A91D5C199D00F1DE01 /* env.h in Headers */,\n\t\t\t\tBA34F9EE1D5C199D00F1DE01 /* testharness.h in Headers */,\n\t\t\t\tBA34F9B61D5C199D00F1DE01 /* port.h in Headers */,\n\t\t\t\tBA34F98D1D5C199D00F1DE01 /* log_writer.h in Headers */,\n\t\t\t\tBA34F9D71D5C199D00F1DE01 /* coding.h in Headers */,\n\t\t\t\tBA34F9B51D5C199D00F1DE01 /* atomic_pointer.h in Headers */,\n\t\t\t\tBA34F9AB1D5C199D00F1DE01 /* iterator.h in Headers */,\n\t\t\t\tBA34F9E71D5C199D00F1DE01 /* logging.h in Headers */,\n\t\t\t\tBA34F9C71D5C199D00F1DE01 /* iterator_wrapper.h in Headers */,\n\t\t\t\tBA34F9EA1D5C199D00F1DE01 /* posix_logger.h in Headers */,\n\t\t\t\tBA34F9911D5C199D00F1DE01 /* skiplist.h in Headers */,\n\t\t\t\tBA34F9C21D5C199D00F1DE01 /* filter_block.h in Headers */,\n\t\t\t\tBA34F9DB1D5C199D00F1DE01 /* crc32c.h in Headers */,\n\t\t\t\tBA34F9A61D5C199D00F1DE01 /* comparator.h in Headers */,\n\t\t\t\tBA34F9AA1D5C199D00F1DE01 /* filter_policy.h in Headers */,\n\t\t\t\tBA34F9F01D5C199D00F1DE01 /* testutil.h in Headers */,\n\t\t\t\tBA34F9BA1D5C199D00F1DE01 /* thread_annotations.h in Headers */,\n\t\t\t\tBA34F9A41D5C199D00F1DE01 /* c.h in Headers */,\n\t\t\t\tBA34F9E51D5C199D00F1DE01 /* histogram.h in Headers */,\n\t\t\t\tBA34F98A1D5C199D00F1DE01 /* log_reader.h in Headers */,\n\t\t\t\tBA34F97B1D5C199D00F1DE01 /* db_impl.h in Headers */,\n\t\t\t\tBA34F97D1D5C199D00F1DE01 /* db_iter.h in Headers */,\n\t\t\t\tBA34F9C51D5C199D00F1DE01 /* format.h in Headers */,\n\t\t\t\tBA34F98F1D5C199D00F1DE01 /* memtable.h in Headers */,\n\t\t\t\tBA34F9B11D5C199D00F1DE01 /* write_batch.h in Headers */,\n\t\t\t\tBA34F9C01D5C199D00F1DE01 /* block_builder.h in Headers */,\n\t\t\t\tBA34F9D01D5C199D00F1DE01 /* arena.h in Headers */,\n\t\t\t\tBA34F9AC1D5C199D00F1DE01 /* options.h in Headers */,\n\t\t\t\tBA34F9A21D5C199D00F1DE01 /* memenv.h in Headers */,\n\t\t\t\tBA34F99A1D5C199D00F1DE01 /* version_set.h in Headers */,\n\t\t\t\tBA34F9BE1D5C199D00F1DE01 /* block.h in Headers */,\n\t\t\t\tBA34F9E21D5C199D00F1DE01 /* hash.h in Headers */,\n\t\t\t\tBA34F9A51D5C199D00F1DE01 /* cache.h in Headers */,\n\t\t\t\tBA34F9E81D5C199D00F1DE01 /* mutexlock.h in Headers */,\n\t\t\t\tBA34F9CE1D5C199D00F1DE01 /* two_level_iterator.h in Headers */,\n\t\t\t\tBA34F9951D5C199D00F1DE01 /* table_cache.h in Headers */,\n\t\t\t\tBA34F9B91D5C199D00F1DE01 /* port_posix.h in Headers */,\n\t\t\t\tBA34F9881D5C199D00F1DE01 /* log_format.h in Headers */,\n\t\t\t\tBA34F9AF1D5C199D00F1DE01 /* table.h in Headers */,\n\t\t\t\tBA34F9B71D5C199D00F1DE01 /* port_example.h in Headers */,\n\t\t\t\tBA34F9851D5C199D00F1DE01 /* filename.h in Headers */,\n\t\t\t\tBA34F9971D5C199D00F1DE01 /* version_edit.h in Headers */,\n\t\t\t\tBA34F9C91D5C199D00F1DE01 /* merger.h in Headers */,\n\t\t\t\tBA34F99D1D5C199D00F1DE01 /* write_batch_internal.h in Headers */,\n\t\t\t\tBA34F9AE1D5C199D00F1DE01 /* status.h in Headers */,\n\t\t\t\tBA34F9A71D5C199D00F1DE01 /* db.h in Headers */,\n\t\t\t\tBA34F9751D5C199D00F1DE01 /* builder.h in Headers */,\n\t\t\t\tBA34F9931D5C199D00F1DE01 /* snapshot.h in Headers */,\n\t\t\t\tBA34F9EB1D5C199D00F1DE01 /* random.h in Headers */,\n\t\t\t\tBA34F9B01D5C199D00F1DE01 /* table_builder.h in Headers */,\n\t\t\t\tBA34F9AD1D5C199D00F1DE01 /* slice.h in Headers */,\n\t\t\t\tBA34F9A81D5C199D00F1DE01 /* dumpfile.h in Headers */,\n\t\t\t\tBA34F9801D5C199D00F1DE01 /* dbformat.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA4CD2171CEB54F000F68F67 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA4CD2291CEB54FF00F68F67 /* QListModule.h in Headers */,\n\t\t\t\tBA4CD22D1CEB54FF00F68F67 /* QSetModule.h in Headers */,\n\t\t\t\tBA4CD2271CEB54FF00F68F67 /* QHashModule.h in Headers */,\n\t\t\t\tBA4CD22B1CEB54FF00F68F67 /* QModuleInit.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA6E47A31F99064D00EFBE6F /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA6E47B31F9906A400EFBE6F /* proto.h in Headers */,\n\t\t\t\tBA6E47B41F9906A400EFBE6F /* recordio.h in Headers */,\n\t\t\t\tBA6E47B51F9906A400EFBE6F /* zookeeper_version.h in Headers */,\n\t\t\t\tBA6E47B61F9906A400EFBE6F /* zookeeper.jute.h in Headers */,\n\t\t\t\tBA6E47B71F9906A400EFBE6F /* ZookeeperConn.h in Headers */,\n\t\t\t\tBA6E47B11F9906A400EFBE6F /* QClusterClient.h in Headers */,\n\t\t\t\tBA6E47B21F9906A400EFBE6F /* QClusterInterface.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAD0C5D719E79FF7005B3784 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBAD0C60F19E7A02C005B3784 /* QSortedSet.h in Headers */,\n\t\t\t\tBAD0C60919E7A02C005B3784 /* QList.h in Headers */,\n\t\t\t\tBADD49B61CCA68BA0093257E /* QProtoParser.h in Headers */,\n\t\t\t\tBA6C336D19EA77B200AC1800 /* QGlobRegex.h in Headers */,\n\t\t\t\tBA3B0AD71ACBA9E1000D9399 /* QAOF.h in Headers */,\n\t\t\t\tBA47763E1AB0773F00EDBB5D /* redisIntset.h in Headers */,\n\t\t\t\tBAD0C61519E7A02C005B3784 /* QString.h in Headers */,\n\t\t\t\tBA34F9FA1D5C25E400F1DE01 /* QLeveldb.h in Headers */,\n\t\t\t\tBAD0C60D19E7A02C005B3784 /* QSet.h in Headers */,\n\t\t\t\tBAD0C60419E7A02C005B3784 /* QCommon.h in Headers */,\n\t\t\t\tBAD0C61719E7A02C005B3784 /* QHelper.h in Headers */,\n\t\t\t\tBAD0C60219E7A02C005B3784 /* QCommand.h in Headers */,\n\t\t\t\tBAD0C60619E7A02C005B3784 /* QHash.h in Headers */,\n\t\t\t\tBAFFCB631A9EA0AC00C47F92 /* redisZipList.h in Headers */,\n\t\t\t\tBAFFCB541A8F09CA00C47F92 /* QDB.h in Headers */,\n\t\t\t\tBAD0C61319E7A02C005B3784 /* QStore.h in Headers */,\n\t\t\t\tBA98F8421B1B539600ABCBD9 /* QSlowLog.h in Headers */,\n\t\t\t\tBA4053941BA1AE3B00B93053 /* QReplication.h in Headers */,\n\t\t\t\tBA29C44F19FB2CEE00796E3A /* QMulti.h in Headers */,\n\t\t\t\tBA4CD2331CEB61E100F68F67 /* QModule.h in Headers */,\n\t\t\t\tBA98F83E1B01B16600ABCBD9 /* QConfig.h in Headers */,\n\t\t\t\tBA34F9F81D5C25E400F1DE01 /* QDumpInterface.h in Headers */,\n\t\t\t\tBAD0C60019E7A02C005B3784 /* QClient.h in Headers */,\n\t\t\t\tBA6C337119EC0D9900AC1800 /* QPubsub.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAFC83FC19C44AE800C18B22 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBAFC84BC19C44AFD00C18B22 /* TaskManager.h in Headers */,\n\t\t\t\tBA2C0C7B1CDC1DC800BDF050 /* Delegate.h in Headers */,\n\t\t\t\tBAFC84C519C44AFD00C18B22 /* Timer.h in Headers */,\n\t\t\t\tBAFC84C719C44AFD00C18B22 /* UnboundedBuffer.h in Headers */,\n\t\t\t\tBAFFCB5C1A9244B200C47F92 /* lzf.h in Headers */,\n\t\t\t\tBAFC84BA19C44AFD00C18B22 /* StreamSocket.h in Headers */,\n\t\t\t\tBAFC84C319C44AFD00C18B22 /* ThreadPool.h in Headers */,\n\t\t\t\tBAFC847419C44AFD00C18B22 /* ClientSocket.h in Headers */,\n\t\t\t\tBAFC847E19C44AFD00C18B22 /* Logger.h in Headers */,\n\t\t\t\tBAFC847A19C44AFD00C18B22 /* Kqueue.h in Headers */,\n\t\t\t\tBAFC84B819C44AFD00C18B22 /* Socket.h in Headers */,\n\t\t\t\tBAFC848F19C44AFD00C18B22 /* NetThreadPool.h in Headers */,\n\t\t\t\tBAFC847219C44AFD00C18B22 /* Buffer.h in Headers */,\n\t\t\t\tBA1250721A6D43D500A0532D /* MemoryFile.h in Headers */,\n\t\t\t\tBA1250821A7338CD00A0532D /* AsyncBuffer.h in Headers */,\n\t\t\t\tBAFC847819C44AFD00C18B22 /* EPoller.h in Headers */,\n\t\t\t\tBAFC849019C44AFD00C18B22 /* Poller.h in Headers */,\n\t\t\t\tBAFC849219C44AFD00C18B22 /* Server.h in Headers */,\n\t\t\t\tBAFFCB5D1A9244B200C47F92 /* lzfP.h in Headers */,\n\t\t\t\tBAFC847C19C44AFD00C18B22 /* ListenSocket.h in Headers */,\n\t\t\t\tBAFC847619C44AFD00C18B22 /* ConfigParser.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tBA34F6021D5C151700F1DE01 /* leveldb */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BA34F6041D5C151800F1DE01 /* Build configuration list for PBXNativeTarget \"leveldb\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBA8B10761F7E691A004F99D0 /* ShellScript */,\n\t\t\t\tBA34F5FF1D5C151700F1DE01 /* Sources */,\n\t\t\t\tBA34F6001D5C151700F1DE01 /* Frameworks */,\n\t\t\t\tBA34F6011D5C151700F1DE01 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = leveldb;\n\t\t\tproductName = leveldb;\n\t\t\tproductReference = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */;\n\t\t\tproductType = \"com.apple.product-type.library.dynamic\";\n\t\t};\n\t\tBA4CD2181CEB54F000F68F67 /* qedismodule */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BA4CD21A1CEB54F000F68F67 /* Build configuration list for PBXNativeTarget \"qedismodule\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBA4CD2151CEB54F000F68F67 /* Sources */,\n\t\t\t\tBA4CD2161CEB54F000F68F67 /* Frameworks */,\n\t\t\t\tBA4CD2171CEB54F000F68F67 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBA4CD22F1CEB55B400F68F67 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = qedismodule;\n\t\t\tproductName = qedismodule;\n\t\t\tproductReference = BA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */;\n\t\t\tproductType = \"com.apple.product-type.library.dynamic\";\n\t\t};\n\t\tBA6E47A41F99064D00EFBE6F /* qcluster */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BA6E47A61F99064D00EFBE6F /* Build configuration list for PBXNativeTarget \"qcluster\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBA6E47A11F99064D00EFBE6F /* Sources */,\n\t\t\t\tBA6E47A21F99064D00EFBE6F /* Frameworks */,\n\t\t\t\tBA6E47A31F99064D00EFBE6F /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBA6E47AA1F99066700EFBE6F /* PBXTargetDependency */,\n\t\t\t\tBA6E47AC1F99066700EFBE6F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = qcluster;\n\t\t\tproductName = qcluster;\n\t\t\tproductReference = BA6E47A51F99064D00EFBE6F /* libqcluster.dylib */;\n\t\t\tproductType = \"com.apple.product-type.library.dynamic\";\n\t\t};\n\t\tBABEBED419C4474900010636 /* Qedis */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BABEBEDE19C4474900010636 /* Build configuration list for PBXNativeTarget \"Qedis\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBABEBED119C4474900010636 /* Sources */,\n\t\t\t\tBABEBED219C4474900010636 /* Frameworks */,\n\t\t\t\tBABEBED319C4474900010636 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBA34F8DE1D5C191300F1DE01 /* PBXTargetDependency */,\n\t\t\t\tBAD0C5E019E7A007005B3784 /* PBXTargetDependency */,\n\t\t\t\tBAFC84D519C44B6800C18B22 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Qedis;\n\t\t\tproductName = Qedis;\n\t\t\tproductReference = BABEBED519C4474900010636 /* Qedis */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n\t\tBAD0C5D819E79FF7005B3784 /* qedislib */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BAD0C5DA19E79FF7005B3784 /* Build configuration list for PBXNativeTarget \"qedislib\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBAD0C5D519E79FF7005B3784 /* Sources */,\n\t\t\t\tBAD0C5D619E79FF7005B3784 /* Frameworks */,\n\t\t\t\tBAD0C5D719E79FF7005B3784 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBA34F8DC1D5C190900F1DE01 /* PBXTargetDependency */,\n\t\t\t\tBAD0C5DE19E7A002005B3784 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = qedislib;\n\t\t\tproductName = qedislib;\n\t\t\tproductReference = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n\t\tBADC7D7019D6BC700036858E /* UnitTest */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BADC7D7719D6BC700036858E /* Build configuration list for PBXNativeTarget \"UnitTest\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBADC7D6D19D6BC700036858E /* Sources */,\n\t\t\t\tBADC7D6E19D6BC700036858E /* Frameworks */,\n\t\t\t\tBADC7D6F19D6BC700036858E /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBAD0C62319E7A88D005B3784 /* PBXTargetDependency */,\n\t\t\t\tBAD0C62119E7A889005B3784 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = UnitTest;\n\t\t\tproductName = UnitTest;\n\t\t\tproductReference = BADC7D7119D6BC700036858E /* UnitTest */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n\t\tBAFC83FD19C44AE800C18B22 /* qbaselib */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BAFC840119C44AE800C18B22 /* Build configuration list for PBXNativeTarget \"qbaselib\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBAFC83FA19C44AE800C18B22 /* Sources */,\n\t\t\t\tBAFC83FB19C44AE800C18B22 /* Frameworks */,\n\t\t\t\tBAFC83FC19C44AE800C18B22 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = qbaselib;\n\t\t\tproductName = qbaselib;\n\t\t\tproductReference = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tBABEBECD19C4474900010636 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0610;\n\t\t\t\tORGANIZATIONNAME = \"Bert Young\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tBA34F6021D5C151700F1DE01 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 6.1.1;\n\t\t\t\t\t};\n\t\t\t\t\tBA4CD2181CEB54F000F68F67 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 6.1.1;\n\t\t\t\t\t};\n\t\t\t\t\tBA6E47A41F99064D00EFBE6F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 8.3.3;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = BABEBED019C4474900010636 /* Build configuration list for PBXProject \"Qedis\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t);\n\t\t\tmainGroup = BABEBECC19C4474900010636;\n\t\t\tproductRefGroup = BABEBED619C4474900010636 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tBABEBED419C4474900010636 /* Qedis */,\n\t\t\t\tBAFC83FD19C44AE800C18B22 /* qbaselib */,\n\t\t\t\tBADC7D7019D6BC700036858E /* UnitTest */,\n\t\t\t\tBAD0C5D819E79FF7005B3784 /* qedislib */,\n\t\t\t\tBA4CD2181CEB54F000F68F67 /* qedismodule */,\n\t\t\t\tBA34F6021D5C151700F1DE01 /* leveldb */,\n\t\t\t\tBA6E47A41F99064D00EFBE6F /* qcluster */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tBA8B10761F7E691A004F99D0 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"if [ ! -d \\\"leveldb\\\" ]; then\\n    echo \\\"leveldb not present. Fetching leveldb-1.18 from internet...\\\";\\n    curl -s -L -O https://github.com/google/leveldb/archive/v1.18.tar.gz;\\n    tar xzvf v1.18.tar.gz;\\n    rm -f v1.18.tar.gz;\\n    mv leveldb-1.18 leveldb;\\nfi\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tBA34F5FF1D5C151700F1DE01 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA34F9CD1D5C199D00F1DE01 /* two_level_iterator.cc in Sources */,\n\t\t\t\tBA34F9EF1D5C199D00F1DE01 /* testutil.cc in Sources */,\n\t\t\t\tBA34F99C1D5C199D00F1DE01 /* write_batch.cc in Sources */,\n\t\t\t\tBA34F97F1D5C199D00F1DE01 /* dbformat.cc in Sources */,\n\t\t\t\tBA34F9C11D5C199D00F1DE01 /* filter_block.cc in Sources */,\n\t\t\t\tBA34F9841D5C199D00F1DE01 /* filename.cc in Sources */,\n\t\t\t\tBA34F9A11D5C199D00F1DE01 /* memenv.cc in Sources */,\n\t\t\t\tBA34F9C81D5C199D00F1DE01 /* merger.cc in Sources */,\n\t\t\t\tBA34F9CA1D5C199D00F1DE01 /* table.cc in Sources */,\n\t\t\t\tBA34F9DA1D5C199D00F1DE01 /* crc32c.cc in Sources */,\n\t\t\t\tBA34F9821D5C199D00F1DE01 /* dumpfile.cc in Sources */,\n\t\t\t\tBA34F9961D5C199D00F1DE01 /* version_edit.cc in Sources */,\n\t\t\t\tBA34F9C61D5C199D00F1DE01 /* iterator.cc in Sources */,\n\t\t\t\tBA34F98C1D5C199D00F1DE01 /* log_writer.cc in Sources */,\n\t\t\t\tBA34F9DE1D5C199D00F1DE01 /* env_posix.cc in Sources */,\n\t\t\t\tBA34F9E41D5C199D00F1DE01 /* histogram.cc in Sources */,\n\t\t\t\tBA34F9BD1D5C199D00F1DE01 /* block.cc in Sources */,\n\t\t\t\tBA34F9E91D5C199D00F1DE01 /* options.cc in Sources */,\n\t\t\t\tBA34F9ED1D5C199D00F1DE01 /* testharness.cc in Sources */,\n\t\t\t\tBA34F9D61D5C199D00F1DE01 /* coding.cc in Sources */,\n\t\t\t\tBA34F9D91D5C199D00F1DE01 /* comparator.cc in Sources */,\n\t\t\t\tBA34F9CF1D5C199D00F1DE01 /* arena.cc in Sources */,\n\t\t\t\tBA34F9891D5C199D00F1DE01 /* log_reader.cc in Sources */,\n\t\t\t\tBA34F9741D5C199D00F1DE01 /* builder.cc in Sources */,\n\t\t\t\tBA34F9CB1D5C199D00F1DE01 /* table_builder.cc in Sources */,\n\t\t\t\tBA34F9941D5C199D00F1DE01 /* table_cache.cc in Sources */,\n\t\t\t\tBA34F9761D5C199D00F1DE01 /* c.cc in Sources */,\n\t\t\t\tBA34F9D41D5C199D00F1DE01 /* cache.cc in Sources */,\n\t\t\t\tBA34F97A1D5C199D00F1DE01 /* db_impl.cc in Sources */,\n\t\t\t\tBA34F9E61D5C199D00F1DE01 /* logging.cc in Sources */,\n\t\t\t\tBA34F9BF1D5C199D00F1DE01 /* block_builder.cc in Sources */,\n\t\t\t\tBA34F9901D5C199D00F1DE01 /* repair.cc in Sources */,\n\t\t\t\tBA34F9D21D5C199D00F1DE01 /* bloom.cc in Sources */,\n\t\t\t\tBA34F9791D5C199D00F1DE01 /* db_bench.cc in Sources */,\n\t\t\t\tBA34F9DD1D5C199D00F1DE01 /* env.cc in Sources */,\n\t\t\t\tBA34F9B81D5C199D00F1DE01 /* port_posix.cc in Sources */,\n\t\t\t\tBA34F9C41D5C199D00F1DE01 /* format.cc in Sources */,\n\t\t\t\tBA34F9EC1D5C199D00F1DE01 /* status.cc in Sources */,\n\t\t\t\tBA34F9E01D5C199D00F1DE01 /* filter_policy.cc in Sources */,\n\t\t\t\tBA34F9991D5C199D00F1DE01 /* version_set.cc in Sources */,\n\t\t\t\tBA34F9E11D5C199D00F1DE01 /* hash.cc in Sources */,\n\t\t\t\tBA34F98E1D5C199D00F1DE01 /* memtable.cc in Sources */,\n\t\t\t\tBA34F97C1D5C199D00F1DE01 /* db_iter.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA4CD2151CEB54F000F68F67 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA4CD22C1CEB54FF00F68F67 /* QSetModule.cc in Sources */,\n\t\t\t\tBA4CD22A1CEB54FF00F68F67 /* QModuleInit.cc in Sources */,\n\t\t\t\tBA4CD2261CEB54FF00F68F67 /* QHashModule.cc in Sources */,\n\t\t\t\tBA4CD2281CEB54FF00F68F67 /* QListModule.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBA6E47A11F99064D00EFBE6F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA6E47AD1F99068A00EFBE6F /* QClusterClient.cc in Sources */,\n\t\t\t\tBA6E47AE1F99068A00EFBE6F /* recordio.c in Sources */,\n\t\t\t\tBA6E47AF1F99068A00EFBE6F /* zookeeper.jute.c in Sources */,\n\t\t\t\tBA6E47B01F99068A00EFBE6F /* ZookeeperConn.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBABEBED119C4474900010636 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA6E47A01F9905F500EFBE6F /* ZookeeperConn.cc in Sources */,\n\t\t\t\tBAB2265E2007B7B7005241F6 /* QMigration.cc in Sources */,\n\t\t\t\tBA6E479F1F9905F500EFBE6F /* zookeeper.jute.c in Sources */,\n\t\t\t\tBA6E478E1F99056000EFBE6F /* QSlaveClient.cc in Sources */,\n\t\t\t\tBA6E47951F9905E600EFBE6F /* QClusterClient.cc in Sources */,\n\t\t\t\tBA6E479E1F9905F500EFBE6F /* recordio.c in Sources */,\n\t\t\t\tBAD0C61A19E7A075005B3784 /* Qedis.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAD0C5D519E79FF7005B3784 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA4CD2321CEB61E100F68F67 /* QModule.cc in Sources */,\n\t\t\t\tBA47763D1AB0773F00EDBB5D /* redisIntset.c in Sources */,\n\t\t\t\tBA98F8411B1B539600ABCBD9 /* QSlowLog.cc in Sources */,\n\t\t\t\tBA4053931BA1AE3B00B93053 /* QReplication.cc in Sources */,\n\t\t\t\tBAD0C61619E7A02C005B3784 /* QHelper.cc in Sources */,\n\t\t\t\tBA6C336C19EA77B200AC1800 /* QGlobRegex.cc in Sources */,\n\t\t\t\tBA3B0AD61ACBA9E1000D9399 /* QAOF.cc in Sources */,\n\t\t\t\tBAD0C61219E7A02C005B3784 /* QStore.cc in Sources */,\n\t\t\t\tBA98F83D1B01B16600ABCBD9 /* QConfig.cc in Sources */,\n\t\t\t\tBAFFCB531A8F09CA00C47F92 /* QDB.cc in Sources */,\n\t\t\t\tBAFFCB5F1A9423DC00C47F92 /* crc64.c in Sources */,\n\t\t\t\tBAD0C60C19E7A02C005B3784 /* QSet.cc in Sources */,\n\t\t\t\tBAD0C60A19E7A02C005B3784 /* QPubsub.cc in Sources */,\n\t\t\t\tBAD0C60519E7A02C005B3784 /* QHash.cc in Sources */,\n\t\t\t\tBAD0C60319E7A02C005B3784 /* QCommon.cc in Sources */,\n\t\t\t\tBAFFCB621A9EA0AC00C47F92 /* redisZipList.c in Sources */,\n\t\t\t\tBAD0C5FF19E7A02C005B3784 /* QClient.cc in Sources */,\n\t\t\t\tBAD0C61419E7A02C005B3784 /* QString.cc in Sources */,\n\t\t\t\tBAD0C60719E7A02C005B3784 /* QKeyCommand.cc in Sources */,\n\t\t\t\tBAD0C60819E7A02C005B3784 /* QList.cc in Sources */,\n\t\t\t\tBA34F9F91D5C25E400F1DE01 /* QLeveldb.cc in Sources */,\n\t\t\t\tBAD0C60E19E7A02C005B3784 /* QSortedSet.cc in Sources */,\n\t\t\t\tBA29C44E19FB2CEE00796E3A /* QMulti.cc in Sources */,\n\t\t\t\tBADD49B51CCA68BA0093257E /* QProtoParser.cc in Sources */,\n\t\t\t\tBAD0C60B19E7A02C005B3784 /* QServerCommand.cc in Sources */,\n\t\t\t\tBAD0C60119E7A02C005B3784 /* QCommand.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBADC7D6D19D6BC700036858E /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBA6C336F19EA77C100AC1800 /* QGlobRegex_unittest.cc in Sources */,\n\t\t\t\tBADC7D7C19D6BC960036858E /* UnitTest.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBAFC83FA19C44AE800C18B22 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBAFC847519C44AFD00C18B22 /* ConfigParser.cc in Sources */,\n\t\t\t\tBAFFCB5A1A9244B200C47F92 /* lzf_c.c in Sources */,\n\t\t\t\tBAFC84C219C44AFD00C18B22 /* ThreadPool.cc in Sources */,\n\t\t\t\tBAFC847919C44AFD00C18B22 /* Kqueue.cc in Sources */,\n\t\t\t\tBA1250711A6D43D500A0532D /* MemoryFile.cc in Sources */,\n\t\t\t\tBAFC849119C44AFD00C18B22 /* Server.cc in Sources */,\n\t\t\t\tBAFC848E19C44AFD00C18B22 /* NetThreadPool.cc in Sources */,\n\t\t\t\tBAFC84B719C44AFD00C18B22 /* Socket.cc in Sources */,\n\t\t\t\tBAFC847319C44AFD00C18B22 /* ClientSocket.cc in Sources */,\n\t\t\t\tBAFC847719C44AFD00C18B22 /* EPoller.cc in Sources */,\n\t\t\t\tBAFFCB5B1A9244B200C47F92 /* lzf_d.c in Sources */,\n\t\t\t\tBAFC847B19C44AFD00C18B22 /* ListenSocket.cc in Sources */,\n\t\t\t\tBAFC84C619C44AFD00C18B22 /* UnboundedBuffer.cc in Sources */,\n\t\t\t\tBA1250811A7338CD00A0532D /* AsyncBuffer.cc in Sources */,\n\t\t\t\tBAFC84BB19C44AFD00C18B22 /* TaskManager.cc in Sources */,\n\t\t\t\tBAFC84B919C44AFD00C18B22 /* StreamSocket.cc in Sources */,\n\t\t\t\tBAFC84C419C44AFD00C18B22 /* Timer.cc in Sources */,\n\t\t\t\tBAFC847D19C44AFD00C18B22 /* Logger.cc in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tBA34F8DC1D5C190900F1DE01 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BA34F6021D5C151700F1DE01 /* leveldb */;\n\t\t\ttargetProxy = BA34F8DB1D5C190900F1DE01 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBA34F8DE1D5C191300F1DE01 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BA34F6021D5C151700F1DE01 /* leveldb */;\n\t\t\ttargetProxy = BA34F8DD1D5C191300F1DE01 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBA4CD22F1CEB55B400F68F67 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAD0C5D819E79FF7005B3784 /* qedislib */;\n\t\t\ttargetProxy = BA4CD22E1CEB55B400F68F67 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBA6E47AA1F99066700EFBE6F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAFC83FD19C44AE800C18B22 /* qbaselib */;\n\t\t\ttargetProxy = BA6E47A91F99066700EFBE6F /* PBXContainerItemProxy */;\n\t\t};\n\t\tBA6E47AC1F99066700EFBE6F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAD0C5D819E79FF7005B3784 /* qedislib */;\n\t\t\ttargetProxy = BA6E47AB1F99066700EFBE6F /* PBXContainerItemProxy */;\n\t\t};\n\t\tBAD0C5DE19E7A002005B3784 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAFC83FD19C44AE800C18B22 /* qbaselib */;\n\t\t\ttargetProxy = BAD0C5DD19E7A002005B3784 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBAD0C5E019E7A007005B3784 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAD0C5D819E79FF7005B3784 /* qedislib */;\n\t\t\ttargetProxy = BAD0C5DF19E7A007005B3784 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBAD0C62119E7A889005B3784 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAFC83FD19C44AE800C18B22 /* qbaselib */;\n\t\t\ttargetProxy = BAD0C62019E7A889005B3784 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBAD0C62319E7A88D005B3784 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAD0C5D819E79FF7005B3784 /* qedislib */;\n\t\t\ttargetProxy = BAD0C62219E7A88D005B3784 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBAFC84D519C44B6800C18B22 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BAFC83FD19C44AE800C18B22 /* qbaselib */;\n\t\t\ttargetProxy = BAFC84D419C44B6800C18B22 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin XCBuildConfiguration section */\n\t\tBA34F6051D5C151800F1DE01 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\tLEVELDB_PLATFORM_POSIX,\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\tOS_MACOSX,\n\t\t\t\t);\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/leveldb\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.10;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBA34F6061D5C151800F1DE01 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\tLEVELDB_PLATFORM_POSIX,\n\t\t\t\t\tOS_MACOSX,\n\t\t\t\t);\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/leveldb\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.10;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBA4CD21B1CEB54F000F68F67 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.10;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBA4CD21C1CEB54F000F68F67 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.10;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBA6E47A71F99064D00EFBE6F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.12;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBA6E47A81F99064D00EFBE6F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.12;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBABEBEDC19C4474900010636 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_SYMBOLS_PRIVATE_EXTERN = NO;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\tleveldb,\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\tleveldb/include,\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBABEBEDD19C4474900010636 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\tleveldb,\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\tleveldb/include,\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBABEBEDF19C4474900010636 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QBase\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBABEBEE019C4474900010636 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QBase\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBAD0C5DB19E79FF7005B3784 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tEXECUTABLE_EXTENSION = dylib;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QBase\",\n\t\t\t\t);\n\t\t\t\tMACH_O_TYPE = mh_dylib;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBAD0C5DC19E79FF7005B3784 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tEXECUTABLE_EXTENSION = dylib;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QBase\",\n\t\t\t\t);\n\t\t\t\tMACH_O_TYPE = mh_dylib;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBADC7D7819D6BC700036858E /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QedisCore\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBADC7D7919D6BC700036858E /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,\n\t\t\t\t\t\"$(SRCROOT)/QedisCore\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBAFC83FF19C44AE800C18B22 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tEXECUTABLE_EXTENSION = dylib;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tMACH_O_TYPE = mh_dylib;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBAFC840019C44AE800C18B22 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tEXECUTABLE_EXTENSION = dylib;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tMACH_O_TYPE = mh_dylib;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tBA34F6041D5C151800F1DE01 /* Build configuration list for PBXNativeTarget \"leveldb\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBA34F6051D5C151800F1DE01 /* Debug */,\n\t\t\t\tBA34F6061D5C151800F1DE01 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBA4CD21A1CEB54F000F68F67 /* Build configuration list for PBXNativeTarget \"qedismodule\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBA4CD21B1CEB54F000F68F67 /* Debug */,\n\t\t\t\tBA4CD21C1CEB54F000F68F67 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBA6E47A61F99064D00EFBE6F /* Build configuration list for PBXNativeTarget \"qcluster\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBA6E47A71F99064D00EFBE6F /* Debug */,\n\t\t\t\tBA6E47A81F99064D00EFBE6F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t};\n\t\tBABEBED019C4474900010636 /* Build configuration list for PBXProject \"Qedis\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBABEBEDC19C4474900010636 /* Debug */,\n\t\t\t\tBABEBEDD19C4474900010636 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBABEBEDE19C4474900010636 /* Build configuration list for PBXNativeTarget \"Qedis\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBABEBEDF19C4474900010636 /* Debug */,\n\t\t\t\tBABEBEE019C4474900010636 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBAD0C5DA19E79FF7005B3784 /* Build configuration list for PBXNativeTarget \"qedislib\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBAD0C5DB19E79FF7005B3784 /* Debug */,\n\t\t\t\tBAD0C5DC19E79FF7005B3784 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBADC7D7719D6BC700036858E /* Build configuration list for PBXNativeTarget \"UnitTest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBADC7D7819D6BC700036858E /* Debug */,\n\t\t\t\tBADC7D7919D6BC700036858E /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBAFC840119C44AE800C18B22 /* Build configuration list for PBXNativeTarget \"qbaselib\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBAFC83FF19C44AE800C18B22 /* Debug */,\n\t\t\t\tBAFC840019C44AE800C18B22 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = BABEBECD19C4474900010636 /* Project object */;\n}\n"
  },
  {
    "path": "QedisCore/CMakeLists.txt",
    "content": "AUX_SOURCE_DIRECTORY(. QEDIS_SRC)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nLINK_DIRECTORIES(../../leveldb)\n\nADD_LIBRARY(qediscore SHARED ${QEDIS_SRC})\nSET(LIBRARY_OUTPUT_PATH ../../bin)\n\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/leveldb/include)\n\nADD_DEPENDENCIES(qediscore qbaselib leveldb)\nTARGET_LINK_LIBRARIES(qediscore; qbaselib; dl; leveldb)\n\nSET_TARGET_PROPERTIES(qediscore PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "QedisCore/QAOF.cc",
    "content": "\n#include \"QAOF.h\"\n#include \"Log/Logger.h\"\n#include \"Threads/ThreadPool.h\"\n#include \"QStore.h\"\n#include \"QConfig.h\"\n#include \"QProtoParser.h\"\n#include <unistd.h>\n#include <sstream>\n\nnamespace qedis\n{\n\nconst char* const g_aofTmp = \"qedis_appendonly.aof.tmp\";\n\npid_t             g_rewritePid = -1;\n\n\n/*****************************************************\n * when after fork(), the parent stop aof thread, which means\n * the coming aof data will be write to the tmp buffer, not to\n * aof file.\n ****************************************************/\nvoid QAOFThreadController::RewriteDoneHandler(int exitRet, int whatSignal)\n{\n    g_rewritePid = -1;\n\n    if (exitRet == 0 && whatSignal == 0)\n    {\n        INF << \"save aof success\";\n        ::rename(g_aofTmp, g_config.appendfilename.c_str());\n    }\n    else\n    {\n        ERR << \"save aof failed with exit result \" << exitRet << \", signal \" << whatSignal;\n        ::unlink(g_aofTmp);\n    }\n        \n    QAOFThreadController::Instance().Start();\n}\n\n\nQAOFThreadController& QAOFThreadController::Instance()\n{\n    static QAOFThreadController threadCtrl;\n    return threadCtrl;\n}\n\nbool QAOFThreadController::ProcessTmpBuffer(BufferSequence& bf)\n{\n    aofBuffer_.ProcessBuffer(bf);\n    return bf.count > 0;\n}\n\nvoid QAOFThreadController::SkipTmpBuffer(size_t n)\n{\n    aofBuffer_.Skip(n);\n}\n\n// main thread  call this\nvoid QAOFThreadController::Start()\n{\n    DBG << \"start aof thread\";\n    \n    assert(!aofThread_ || !aofThread_->IsAlive());\n    \n    aofThread_ = std::make_shared<AOFThread>();\n    aofThread_->SetAlive();\n    \n    ThreadPool::Instance().ExecuteTask(std::bind(&AOFThread::Run, aofThread_));\n}\n\n// when fork(), parent call stop;\nvoid QAOFThreadController::Stop()\n{\n    if (!aofThread_)\n        return;\n    \n    DBG << \"stop aof thread\";\n    aofThread_->Stop();\n    QAOFThreadController::Instance().Join();\n    aofThread_ = nullptr;\n}\n\n// main thread call this\nvoid QAOFThreadController::_WriteSelectDB(int db, AsyncBuffer& dst)\n{\n    if (db == lastDb_)\n        return;\n\n    lastDb_ = db;\n    \n    WriteMultiBulkLong(2, dst);\n    WriteBulkString(\"select\", 6, dst);\n    WriteBulkLong(db, dst);\n}\n\nvoid QAOFThreadController::SaveCommand(const std::vector<QString>& params, int db)\n{\n    AsyncBuffer* dst;\n    \n    if (aofThread_ && aofThread_->IsAlive())\n        dst = &aofThread_->buf_;\n    else\n        dst = &aofBuffer_;\n    \n    _WriteSelectDB(db, *dst);\n    qedis::SaveCommand(params, *dst);\n}\n\nQAOFThreadController::AOFThread::~AOFThread()\n{\n    file_.Close();\n}\n\nbool QAOFThreadController::AOFThread::Flush()\n{\n    BufferSequence  data;\n    buf_.ProcessBuffer(data);\n    if (data.count == 0)\n        return false;\n    \n    if (!file_.IsOpen())\n        file_.Open(g_config.appendfilename.c_str());\n    \n    for (size_t i = 0; i < data.count; ++ i)\n    {\n        file_.Write(data.buffers[i].iov_base, data.buffers[i].iov_len);\n    }\n    \n    buf_.Skip(data.TotalBytes());\n    \n    return data.count != 0;\n}\n\n\nvoid QAOFThreadController::AOFThread::SaveCommand(const std::vector<QString>& params)\n{\n    qedis::SaveCommand(params, buf_);\n}\n\nvoid QAOFThreadController::AOFThread::Run()\n{\n    assert (IsAlive());\n\n    // CHECK aof temp buffer first!\n    BufferSequence data;\n    while (QAOFThreadController::Instance().ProcessTmpBuffer(data))\n    {\n        if (!file_.IsOpen())\n            file_.Open(g_config.appendfilename.c_str());\n        \n        for (size_t i = 0; i < data.count; ++ i)\n        {\n            file_.Write(data.buffers[i].iov_base, data.buffers[i].iov_len);\n        }\n        \n        QAOFThreadController::Instance().SkipTmpBuffer(data.TotalBytes());\n    }\n    \n    while (IsAlive())\n    {\n        //sync incrementally, always, the redis sync policy is useless\n        if (Flush())\n            file_.Sync();\n        else\n            std::this_thread::sleep_for(std::chrono::milliseconds(1));\n    }\n    \n    file_.Close();\n    pro_.set_value();\n}\n\nvoid QAOFThreadController::Join()\n{\n    if (aofThread_)\n        aofThread_->pro_.get_future().wait();\n}\n\nstatic void SaveExpire(const QString& key, uint64_t absMs, OutputMemoryFile& file)\n{\n    WriteBulkLong(3, file);\n    WriteBulkString(\"expire\", 6, file);\n    WriteBulkString(key, file);\n    WriteBulkLong(absMs, file);\n}\n\n// child  save the db to tmp file\nstatic void SaveObject(const QString& key, const QObject& obj, OutputMemoryFile& file);\nstatic void RewriteProcess()\n{\n    OutputMemoryFile  file;\n    if (!file.Open(g_aofTmp, false))\n    {\n        perror(\"open tmp failed\");\n        _exit(-1);\n    }\n\n    for (int dbno = 0; true; ++ dbno)\n    {\n        if (QSTORE.SelectDB(dbno) == -1)\n            break;\n        \n        if (QSTORE.DBSize() == 0)\n            continue;\n\n        // select db\n        WriteMultiBulkLong(2, file);\n        WriteBulkString(\"select\", 6, file);\n        WriteBulkLong(dbno, file);\n\n        const auto now = ::Now();\n        for (const auto& kv : QSTORE)\n        {\n            int64_t ttl = QSTORE.TTL(kv.first, now);\n            if (ttl == QStore::ExpireResult::expired)\n                continue;\n\n            SaveObject(kv.first, kv.second, file);\n            if (ttl > 0)\n                SaveExpire(kv.first, ttl + now, file);\n        }\n    }\n}\n\nQError bgrewriteaof(const std::vector<QString>& , UnboundedBuffer* reply)\n{\n    if (g_rewritePid != -1)\n    {\n        reply->PushData(\"-ERR aof already in progress\\r\\n\",\n                 sizeof \"-ERR aof already in progress\\r\\n\" - 1);\n        return QError_ok;\n    }\n    else\n    {\n        g_rewritePid = fork();\n        switch (g_rewritePid)\n        {\n            case 0:\n                RewriteProcess();\n                _exit(0);\n                \n            case -1:\n                ERR << \"fork aof process failed, errno = \" << errno;\n                reply->PushData(\"-ERR aof rewrite failed\\r\\n\",\n                         sizeof \"-ERR aof rewrite failed\\r\\n\" - 1);\n                return QError_ok;\n                \n            default:\n                break;\n        }\n    }\n    \n    QAOFThreadController::Instance().Stop();\n    FormatOK(reply);\n    return QError_ok;\n}\n\nstatic void SaveStringObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    WriteMultiBulkLong(3, file);\n    WriteBulkString(\"set\", 3, file);\n    WriteBulkString(key, file);\n\n    auto str = GetDecodedString(&obj);\n    WriteBulkString(*str, file);\n}\n\nstatic void SaveListObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    auto list = obj.CastList();\n    if (list->empty())\n        return;\n\n    WriteMultiBulkLong(list->size() + 2, file); // rpush listname + elems\n    WriteBulkString(\"rpush\", 5, file);\n    WriteBulkString(key, file);\n\n    for (const auto& elem : *list)\n    {\n        WriteBulkString(elem, file);\n    }\n}\n\nstatic void SaveSetObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    auto set = obj.CastSet();\n    if (set->empty())\n        return;\n\n    WriteMultiBulkLong(set->size() + 2, file); // sadd set_name + elems\n    WriteBulkString(\"sadd\", 4, file);\n    WriteBulkString(key, file);\n\n    for (const auto& elem : *set)\n    {\n        WriteBulkString(elem, file);\n    }\n}\n\nstatic void  SaveZSetObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    auto zset = obj.CastSortedSet();\n    if (zset->Size() == 0)\n        return;\n\n    WriteMultiBulkLong(2 * zset->Size() + 2, file); // zadd zset_name + (score + member)\n    WriteBulkString(\"zadd\", 4, file);\n    WriteBulkString(key, file);\n\n    for (const auto& pair : *zset)\n    {\n        const QString& member = pair.first;\n        double score          = pair.second;\n\n        char scoreStr[32];\n        int  len = Double2Str(scoreStr, sizeof scoreStr, score);\n\n        WriteBulkString(scoreStr, len, file);\n        WriteBulkString(member, file);\n    }\n}\n\nstatic void SaveHashObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    auto hash = obj.CastHash();\n    if (hash->empty())\n        return;\n\n    WriteMultiBulkLong(2* hash->size() + 2, file); // hmset hash_name + (key + value)\n    WriteBulkString(\"hmset\", 5, file);\n    WriteBulkString(key, file);\n\n    for (const auto& pair : *hash)\n    {\n        WriteBulkString(pair.first, file);\n        WriteBulkString(pair.second, file);\n    }\n}\n\n\nstatic void SaveObject(const QString& key, const QObject& obj, OutputMemoryFile& file)\n{\n    switch (obj.type)\n    {\n        case QType_string:\n            SaveStringObject(key, obj, file);\n            break;\n            \n        case QType_list:\n            SaveListObject(key, obj, file);\n            break;\n            \n        case QType_set:\n            SaveSetObject(key, obj, file);\n            break;\n            \n        case QType_sortedSet:\n            SaveZSetObject(key, obj, file);\n            break;\n            \n        case QType_hash:\n            SaveHashObject(key, obj, file);\n            break;\n            \n        default:\n            assert (false);\n            break;\n    }\n}\n\n\nQAOFLoader::QAOFLoader()\n{\n}\n\nbool QAOFLoader::Load(const char* name)\n{\n    if (::access(name, F_OK) != 0)\n        return false;\n\n    {\n        // truncate tail trash zeroes\n        OutputMemoryFile file;\n        file.Open(name);\n        file.TruncateTailZero();\n    }\n    \n    // load file to memory\n    InputMemoryFile file;\n    if (!file.Open(name))\n        return  false;\n\n    size_t maxLen = std::numeric_limits<size_t>::max();\n    const char* content = file.Read(maxLen);\n    \n    if (maxLen == 0)\n        return false;\n\n    QProtoParser parser;\n    // extract commands from file content\n    const char* const end = content + maxLen;\n    while (content < end)\n    {\n        parser.Reset();\n        if (QParseResult::ok != parser.ParseRequest(content, end))\n        {\n            ERR << \"Load aof failed\";\n            return false;\n        }\n\n        cmds_.push_back(parser.GetParams());\n    }\n\n    return true;\n}\n    \n}\n"
  },
  {
    "path": "QedisCore/QAOF.h",
    "content": "#ifndef BERT_QAOF_H\n#define BERT_QAOF_H\n\n#include <memory>\n#include <future>\n#include \"Log/MemoryFile.h\"\n#include \"AsyncBuffer.h\"\n#include \"QString.h\"\n#include \"QStore.h\"\n\nnamespace qedis\n{\n\nextern pid_t g_rewritePid;\n\nclass  QAOFThreadController\n{\npublic:\n    static QAOFThreadController&  Instance();\n    \n    QAOFThreadController(const QAOFThreadController& ) = delete;\n    void  operator= (const QAOFThreadController& ) = delete;\n\n    void  Start();\n    void  Stop();\n    void  Join();\n    \n    void  SaveCommand(const std::vector<QString>& params, int db);\n    bool  ProcessTmpBuffer(BufferSequence& bf);\n    void  SkipTmpBuffer(size_t  n);\n    \n    static void  RewriteDoneHandler(int exit, int signal);\n    \nprivate:\n    QAOFThreadController() : lastDb_(-1) {}\n\n    class AOFThread\n    {\n        friend class QAOFThreadController;\n    public:\n        AOFThread() : alive_(false) { }\n        ~AOFThread();\n        \n        void  SetAlive()      {  alive_ = true; }\n        bool  IsAlive() const {  return alive_; }\n        void  Stop()          {  alive_ = false; }\n        \n        //void  Close();\n        void  SaveCommand(const std::vector<QString>& params);\n        \n        bool  Flush();\n    \n        void  Run();\n        \n        std::atomic<bool>   alive_;\n\n        OutputMemoryFile    file_;\n        AsyncBuffer         buf_;\n        \n        std::promise<void>  pro_; // Effective modern C++ : Item 39\n    };\n    \n    void _WriteSelectDB(int db, AsyncBuffer& dst);\n    \n    std::shared_ptr<AOFThread>  aofThread_;\n    AsyncBuffer                 aofBuffer_;\n    int                         lastDb_;\n};\n\n\nclass  QAOFLoader\n{\npublic:\n    QAOFLoader();\n    \n    bool  Load(const char* name);\n\n    const std::vector<std::vector<QString> >& GetCmds() const\n    {\n        return cmds_;\n    }\n\nprivate:\n    std::vector<std::vector<QString> > cmds_;\n};\n\ntemplate <typename DEST>\ninline void  WriteBulkString(const char* str, size_t strLen, DEST& dst)\n{\n    char    tmp[32];\n    size_t  n = snprintf(tmp, sizeof tmp, \"$%lu\\r\\n\", strLen);\n    \n    dst.Write(tmp, n);\n    dst.Write(str, strLen);\n    dst.Write(\"\\r\\n\", 2);\n}\n\n\ntemplate <typename DEST>\ninline void  WriteBulkString(const QString& str, DEST& dst)\n{\n    WriteBulkString(str.data(), str.size(), dst);\n}\n\ntemplate <typename DEST>\ninline void  WriteMultiBulkLong(long val, DEST& dst)\n{\n    char    tmp[32];\n    size_t  n = snprintf(tmp, sizeof tmp, \"*%lu\\r\\n\", val);\n    dst.Write(tmp, n);\n}\n\ntemplate <typename DEST>\ninline void  WriteBulkLong(long val, DEST& dst)\n{\n    char    tmp[32];\n    size_t  n = snprintf(tmp, sizeof tmp, \"%lu\", val);\n    \n    WriteBulkString(tmp, n, dst);\n}\n\n\ntemplate <typename DEST>\ninline void SaveCommand(const std::vector<QString>& params, DEST& dst)\n{\n    WriteMultiBulkLong(params.size(), dst);\n    \n    for (const auto& s : params)\n    {\n        WriteBulkString(s, dst);\n    }\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "QedisCore/QClient.cc",
    "content": "#include \"Log/Logger.h\"\n\n#include <algorithm>\n#include \"QStore.h\"\n#include \"QCommand.h\"\n#include \"QConfig.h\"\n#include \"QSlowLog.h\"\n#include \"QClient.h\"\n\nnamespace qedis\n{\n\nQClient*  QClient::s_current = 0;\n\nstd::set<std::weak_ptr<QClient>, std::owner_less<std::weak_ptr<QClient> > >\n          QClient::s_monitors;\n\nPacketLength QClient::_ProcessInlineCmd(const char* buf,\n                                        size_t bytes,\n                                        std::vector<QString>& params)\n{\n    if (bytes < 2)\n        return 0;\n\n    QString res;\n\n    for (size_t i = 0; i + 1 < bytes; ++ i)\n    {\n        if (buf[i] == '\\r' && buf[i+1] == '\\n')\n        {\n            if (!res.empty())\n                params.emplace_back(std::move(res));\n\n            return static_cast<PacketLength>(i + 2);\n        }\n\n        if (isblank(buf[i]))\n        {\n            if (!res.empty())\n            {\n                params.reserve(4);\n                params.emplace_back(std::move(res));\n            }\n        }\n        else\n        {\n            res.reserve(16);\n            res.push_back(buf[i]);\n        }\n    }\n\n    return 0;\n}\n\nstatic int ProcessMaster(const char* start, const char* end)\n{\n    auto state = QREPL.GetMasterState();\n\n    switch (state)\n    {\n        case QReplState_connected:\n            // discard all requests before sync;\n            // or continue serve with old data? TODO\n            return static_cast<int>(end - start);\n\n        case QReplState_wait_auth:\n            if (end - start >= 5)\n            {\n                if (strncasecmp(start, \"+OK\\r\\n\", 5) == 0)\n                {\n                    QClient::Current()->SetAuth();\n                    return 5;\n                }\n                else\n                {\n                    assert (!!!\"check masterauth config, master password maybe wrong\");\n                }\n            }\n            else\n            {\n                return 0;\n            }\n            break;\n\n        case QReplState_wait_replconf:\n            if (end - start >= 5)\n            {\n                if (strncasecmp(start, \"+OK\\r\\n\", 5) == 0)\n                {\n                    return 5;\n                }\n                else\n                {\n                    assert (!!!\"check error: send replconf command\");\n                }\n            }\n            else\n            {\n                return 0;\n            }\n            break;\n\n        case QReplState_wait_rdb:\n        {\n            const char* ptr = start;\n            //recv RDB file\n            if (QREPL.GetRdbSize() == std::size_t(-1))\n            {\n                ++ ptr; // skip $\n                int s;\n                if (QParseResult::ok == GetIntUntilCRLF(ptr, end - ptr, s))\n                {\n                    assert (s > 0); // check error for your masterauth or master config\n\n                    QREPL.SetRdbSize(s);\n                    USR << \"recv rdb size \" << s;\n                }\n            }\n            else\n            {\n                std::size_t rdb = static_cast<std::size_t>(end - ptr);\n                QREPL.SaveTmpRdb(ptr, rdb);\n                ptr += rdb;\n            }\n\n            return static_cast<int>(ptr - start);\n        }\n            break;\n            \n        case QReplState_online:\n            break;\n    \n        default:\n            assert(!!!\"wrong master state\");\n            break;\n    }\n    \n    return -1; // do nothing\n}\n\nPacketLength QClient::_HandlePacket(const char* start, std::size_t bytes)\n{\n    s_current = this;\n\n    const char* const end   = start + bytes;\n    const char* ptr  = start;\n    \n    if (GetPeerAddr() == QREPL.GetMasterAddr())\n    {\n        // check slave state\n        auto recved = ProcessMaster(start, end);\n        if (recved != -1)\n            return static_cast<PacketLength>(recved);\n    }\n\n    auto parseRet = parser_.ParseRequest(ptr, end);\n    if (parseRet == QParseResult::error)\n    {\n        if (!parser_.IsInitialState())\n        {\n            this->OnError(); \n            return 0;\n        }\n\n        // try inline command\n        std::vector<QString> params;\n        auto len = _ProcessInlineCmd(ptr, bytes, params); \n        if (len == 0)\n            return 0;\n\n        ptr += len;\n        parser_.SetParams(params);\n        parseRet = QParseResult::ok;\n    }\n    else if (parseRet != QParseResult::ok)\n    {\n        return static_cast<PacketLength>(ptr - start);\n    }\n    \n    QEDIS_DEFER\n    {\n        _Reset();\n    };\n    \n    // handle packet\n    const auto& params = parser_.GetParams();\n    if (params.empty())\n        return static_cast<PacketLength>(ptr - start);\n\n    QString cmd(params[0]);\n    std::transform(params[0].begin(), params[0].end(), cmd.begin(), ::tolower);\n\n    if (!auth_)\n    {\n        if (cmd == \"auth\")\n        {\n            auto now = ::time(nullptr);\n            if (now <= lastauth_ + 1)\n            {\n                // avoid guess password.\n                OnError();\n                return 0;\n            }\n            else\n            {\n                lastauth_ = now;\n            }\n        }\n        else\n        {\n            ReplyError(QError_needAuth, &reply_);\n            SendPacket(reply_);\n            return static_cast<PacketLength>(ptr - start);\n        }\n    }\n    \n    DBG << \"client \" << GetID() << \", cmd \" << cmd;\n    \n    QSTORE.SelectDB(db_);\n    FeedMonitors(params);\n    \n    const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd);\n\n    if (!info)\n    {\n        ReplyError(QError_unknowCmd, &reply_);\n        SendPacket(reply_);\n        return static_cast<PacketLength>(ptr - start);\n    }\n\n    // check transaction\n    if (IsFlagOn(ClientFlag_multi))\n    {\n        if (cmd != \"multi\" &&\n            cmd != \"exec\" &&\n            cmd != \"watch\" &&\n            cmd != \"unwatch\" &&\n            cmd != \"discard\")\n        {\n            if (!info->CheckParamsCount(static_cast<int>(params.size())))\n            {\n                ERR << \"queue failed: cmd \" << cmd.c_str() << \" has params \" << params.size();\n                ReplyError(info ? QError_param : QError_unknowCmd, &reply_);\n                SendPacket(reply_);\n                FlagExecWrong();\n            }\n            else\n            {\n                if (!IsFlagOn(ClientFlag_wrongExec))\n                    queueCmds_.push_back(params);\n                \n                SendPacket(\"+QUEUED\\r\\n\", 9);\n                INF << \"queue cmd \" << cmd.c_str();\n            }\n            \n            return static_cast<PacketLength>(ptr - start);\n        }\n    }\n    \n    // check readonly slave and execute command\n    QError err = QError_ok;\n    if (QREPL.GetMasterState() != QReplState_none &&\n        !IsFlagOn(ClientFlag_master) &&\n        (info->attr & QCommandAttr::QAttr_write))\n    {\n        ReplyError(err = QError_readonlySlave, &reply_);\n    }\n    else\n    {\n        QSlowLog::Instance().Begin();\n        err = QCommandTable::ExecuteCmd(params,\n                                        info,\n                                        IsFlagOn(ClientFlag_master) ? nullptr : &reply_);\n        QSlowLog::Instance().EndAndStat(params);\n    }\n    \n    SendPacket(reply_);\n    \n    if (err == QError_ok && (info->attr & QAttr_write))\n    {\n        Propogate(params);\n    }\n    \n    return static_cast<PacketLength>(ptr - start);\n}\n\nQClient*  QClient::Current()\n{\n    return s_current;\n}\n\nQClient::QClient() : db_(0), flag_(0), name_(\"clientxxx\")\n{\n    auth_ = false;\n    SelectDB(0);\n    _Reset();\n}\n\nvoid QClient::OnConnect()\n{\n    const bool peerIsMaster = (GetPeerAddr() == QREPL.GetMasterAddr());\n\n    if (peerIsMaster)\n    {\n        QREPL.SetMasterState(QReplState_connected);\n        QREPL.SetMaster(std::static_pointer_cast<QClient>(shared_from_this()));\n            \n        SetName(\"MasterConnection\"); \n        SetFlag(ClientFlag_master);\n\n        if (g_config.masterauth.empty())\n            SetAuth();\n    }\n    else\n    {\n        if (g_config.password.empty())\n            SetAuth();\n    }\n}\n\nbool QClient::SelectDB(int db)\n{ \n    if (QSTORE.SelectDB(db) >= 0)\n    {\n        db_ = db;\n        return true;\n    }\n\n    return false;\n}\n\nvoid QClient::_Reset()\n{\n    s_current = 0;\n\n    parser_.Reset();\n    reply_.Clear();\n}\n\nbool QClient::Watch(int dbno, const QString& key)\n{\n    DBG << \"Client \" << name_ << \" watch \" << key.c_str() << \", db \" << dbno;\n    return watchKeys_[dbno].insert(key).second;\n}\n\nbool QClient::NotifyDirty(int dbno, const QString& key)\n{\n    if (IsFlagOn(ClientFlag_dirty))\n    {\n        INF << \"client is already dirty \" << GetID();\n        return true;\n    }\n    \n    if (watchKeys_[dbno].count(key))\n    {\n        INF << GetID() << \" client become dirty because key \" << key << \" in db \" << dbno;\n        SetFlag(ClientFlag_dirty);\n        return true;\n    }\n    else\n    {\n        INF << \"Dirty key is not exist: \" << key << \", because client unwatch before dirty\";\n    }\n    \n    return false;\n}\n\nbool QClient::Exec()\n{\n    QEDIS_DEFER\n    {\n        this->ClearMulti();\n        this->ClearWatch();\n    };\n    \n    if (IsFlagOn(ClientFlag_wrongExec))\n    {\n        return false;\n    }\n    \n    if (IsFlagOn(ClientFlag_dirty))\n    {\n        FormatNullArray(&reply_);\n        return true;\n    }\n    \n    PreFormatMultiBulk(queueCmds_.size(), &reply_);\n    for (const auto& cmd : queueCmds_)\n    {\n        DBG << \"EXEC \" << cmd[0] << \", for client \" << GetID();\n        const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd[0]);\n        QError err = QCommandTable::ExecuteCmd(cmd, info, &reply_);\n        \n        // may dirty clients;\n        if (err == QError_ok && (info->attr & QAttr_write))\n        {\n            Propogate(cmd);\n        }\n    }\n    \n    return true;\n}\n\nvoid QClient::ClearMulti()\n{\n    queueCmds_.clear();\n    ClearFlag(ClientFlag_multi);\n    ClearFlag(ClientFlag_wrongExec);\n}\n    \nvoid QClient::ClearWatch()\n{\n    watchKeys_.clear();\n    ClearFlag(ClientFlag_dirty);\n}\n\n\nbool  QClient::WaitFor(const QString& key, const QString* target)\n{\n    bool  succ = waitingKeys_.insert(key).second;\n    \n    if (succ && target)\n    {\n        if (!target_.empty())\n        {\n            ERR << \"Wait failed for key \" << key << \", because old target \" << target_;\n            waitingKeys_.erase(key);\n            return false;\n        }\n        \n        target_ = *target;\n    }\n    \n    return succ;\n}\n\n\nvoid   QClient::SetSlaveInfo()\n{\n    slaveInfo_.reset(new QSlaveInfo());\n}\n\nvoid  QClient::AddCurrentToMonitor()\n{\n    s_monitors.insert(std::static_pointer_cast<QClient>(s_current->shared_from_this()));\n}\n\nvoid  QClient::FeedMonitors(const std::vector<QString>& params)\n{\n    assert(!params.empty());\n\n    if (s_monitors.empty())\n        return;\n\n    char buf[512];\n    int n = snprintf(buf, sizeof buf, \"+[db%d %s:%hu]: \\\"\",\n             QSTORE.GetDB(),\n             s_current->peerAddr_.GetIP(),\n             s_current->peerAddr_.GetPort());\n\n    assert(n > 0);\n    \n    for (const auto& e : params)\n    {\n        if (static_cast<int>(sizeof buf) > n)\n            n += snprintf(buf + n, sizeof buf - n, \"%s \", e.data());\n        else\n            break;\n    }\n    \n    -- n; // no space follow last param\n    \n    for (auto it(s_monitors.begin()); it != s_monitors.end(); )\n    {\n        auto  m = it->lock();\n        if (m)\n        {\n            m->SendPacket(buf, n);\n            m->SendPacket(\"\\\"\" CRLF, 3);\n            \n            ++ it;\n        }\n        else\n        {\n            s_monitors.erase(it ++);\n        }\n    }\n}\n    \n}\n\n"
  },
  {
    "path": "QedisCore/QClient.h",
    "content": "#ifndef BERT_QCLIENT_H\n#define BERT_QCLIENT_H\n\n#include \"StreamSocket.h\"\n#include \"QCommon.h\"\n\n#include \"QReplication.h\"\n#include \"QProtoParser.h\"\n#include <set>\n#include <unordered_set>\n#include <unordered_map>\n\nnamespace ConnectionTag\n{\n    const int kQedisClient = 1;\n};\n\nnamespace qedis\n{\n\nenum ClientFlag\n{\n    ClientFlag_multi = 0x1,\n    ClientFlag_dirty = 0x1 << 1,\n    ClientFlag_wrongExec = 0x1 << 2,\n    ClientFlag_master = 0x1 << 3,\n};\n\nclass DB;\nstruct QSlaveInfo;\n\nclass QClient: public StreamSocket\n{\nprivate:\n    PacketLength _HandlePacket(const char* msg, std::size_t len) override;\n\npublic:\n    QClient();\n    \n    void OnConnect() override;\n    \n    bool SelectDB(int db);\n    static QClient*  Current();\n    \n    //multi\n    void SetFlag(unsigned flag) { flag_ |= flag; }\n    void ClearFlag(unsigned flag) { flag_ &= ~flag; }\n    bool IsFlagOn(unsigned flag) { return flag_ & flag; }\n    void FlagExecWrong() { if (IsFlagOn(ClientFlag_multi)) SetFlag(ClientFlag_wrongExec);   }\n    \n    bool Watch(int dbno, const QString& key);\n    bool NotifyDirty(int dbno, const QString& key);\n    bool Exec();\n    void ClearMulti();\n    void ClearWatch();\n\n    // pubsub\n    std::size_t Subscribe(const QString& channel)\n    {\n        return  channels_.insert(channel).second ? 1 : 0;\n    }\n\n    std::size_t UnSubscribe(const QString& channel)\n    {\n        return channels_.erase(channel);\n    }\n\n    std::size_t PSubscribe(const QString& channel)\n    {\n        return  patternChannels_.insert(channel).second ? 1 : 0;\n    }\n\n    std::size_t PUnSubscribe(const QString& channel)\n    {\n        return patternChannels_.erase(channel);\n    }\n\n    const std::unordered_set<QString>&  GetChannels() const { return channels_; }\n    const std::unordered_set<QString>&  GetPatternChannels() const { return patternChannels_; }\n    std::size_t     ChannelCount() const { return channels_.size(); }\n    std::size_t     PatternChannelCount() const { return patternChannels_.size(); }\n    \n    bool  WaitFor(const QString& key, const QString* target = nullptr);\n    \n    const std::unordered_set<QString>  WaitingKeys() const { return waitingKeys_; }\n    void  ClearWaitingKeys()    {  waitingKeys_.clear(), target_.clear(); }\n    const QString&  GetTarget() const { return target_; }\n    \n    void  SetName(const QString& name) { name_ = name; }\n    const QString&     GetName() const { return name_; }\n    \n    void         SetSlaveInfo();\n    QSlaveInfo*  GetSlaveInfo() const { return slaveInfo_.get(); }\n    \n    static void  AddCurrentToMonitor();\n    static void  FeedMonitors(const std::vector<QString>& params);\n    \n    void SetAuth() { auth_ = true; }\n    bool GetAuth() const { return auth_; }\n    void RewriteCmd(std::vector<QString>& params) { parser_.SetParams(params); }\n\nprivate:\n    PacketLength _ProcessInlineCmd(const char* , size_t, std::vector<QString>& );\n    void _Reset();\n\n    QProtoParser parser_;\n    UnboundedBuffer reply_;\n\n    int db_;\n\n    std::unordered_set<QString>  channels_;\n    std::unordered_set<QString>  patternChannels_;\n    \n    unsigned flag_;\n    std::unordered_map<int, std::unordered_set<QString> > watchKeys_;\n    std::vector<std::vector<QString> > queueCmds_;\n    \n    // blocked list\n    std::unordered_set<QString> waitingKeys_;\n    QString target_;\n    \n    // slave info from master view\n    std::unique_ptr<QSlaveInfo>  slaveInfo_;\n    \n    // name\n    std::string name_;\n    \n    // auth\n    bool  auth_;\n    time_t lastauth_ = 0;\n    \n    static  QClient*  s_current;\n    static  std::set<std::weak_ptr<QClient>, std::owner_less<std::weak_ptr<QClient> > > s_monitors;\n};\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QCommand.cc",
    "content": "#include \"QCommand.h\"\n#include \"QReplication.h\"\n\nusing std::size_t;\n\nnamespace qedis\n{\n\nconst QCommandInfo QCommandTable::s_info[] =\n{\n    // key\n    {\"type\",        QAttr_read,                2,  &type},\n    {\"exists\",      QAttr_read,                2,  &exists},\n    {\"del\",         QAttr_write,              -2,  &del},\n    {\"expire\",      QAttr_read,                3,  &expire},\n    {\"ttl\",         QAttr_read,                2,  &ttl},\n    {\"pexpire\",     QAttr_read,                3,  &pexpire},\n    {\"pttl\",        QAttr_read,                2,  &pttl},\n    {\"expireat\",    QAttr_read,                3,  &expireat},\n    {\"pexpireat\",   QAttr_read,                3,  &pexpireat},\n    {\"persist\",     QAttr_read,                2,  &persist},\n    {\"move\",        QAttr_write,               3,  &move},\n    {\"keys\",        QAttr_read,                2,  &keys},\n    {\"randomkey\",   QAttr_read,                1,  &randomkey},\n    {\"rename\",      QAttr_write,               3,  &rename},\n    {\"renamenx\",    QAttr_write,               3,  &renamenx},\n    {\"scan\",        QAttr_read,               -2,  &scan},\n    {\"sort\",        QAttr_read,               -2,  &sort},\n    {\"dump\",        QAttr_read,                2,  &dump},\n    {\"restore\",     QAttr_write,              -4,  &restore},\n    {\"migrate\",     QAttr_read,               -6,  &migrate},\n\n    // server\n    {\"select\",      QAttr_read,                2,  &select},\n    {\"dbsize\",      QAttr_read,                1,  &dbsize},\n    {\"bgsave\",      QAttr_read,                1,  &bgsave},\n    {\"save\",        QAttr_read,                1,  &save},\n    {\"lastsave\",    QAttr_read,                1,  &lastsave},\n    {\"flushdb\",     QAttr_write,               1,  &flushdb},\n    {\"flushall\",    QAttr_write,               1,  &flushall},\n    {\"client\",      QAttr_read,               -2,  &client },\n    {\"debug\",       QAttr_read,               -2,  &debug},\n    {\"shutdown\",    QAttr_read,               -1,  &shutdown},\n    {\"bgrewriteaof\",QAttr_read,                1,  &bgrewriteaof},\n    {\"ping\",        QAttr_read,                1,  &ping},\n    {\"echo\",        QAttr_read,                2,  &echo},\n    {\"info\",        QAttr_read,               -1,  &info},\n    {\"monitor\",     QAttr_read,                1,  &monitor},\n    {\"auth\",        QAttr_read,                2,  &auth},\n    {\"slowlog\",     QAttr_read,               -2,  &slowlog},\n    {\"config\",      QAttr_read,               -3,  &config},\n    \n    // string\n    {\"strlen\",      QAttr_read,                2,  &strlen},\n    {\"set\",         QAttr_write,               3,  &set},\n    {\"mset\",        QAttr_write,              -3,  &mset},\n    {\"msetnx\",      QAttr_write,              -3,  &msetnx},\n    {\"setnx\",       QAttr_write,               3,  &setnx},\n    {\"setex\",       QAttr_write,               4,  &setex},\n    {\"psetex\",      QAttr_write,               4,  &psetex},\n    {\"get\",         QAttr_read,                2,  &get},\n    {\"getset\",      QAttr_write,               3,  &getset},\n    {\"mget\",        QAttr_read,               -2,  &mget},\n    {\"append\",      QAttr_write,               3,  &append},\n    {\"bitcount\",    QAttr_read,               -2,  &bitcount},\n    {\"bitop\",       QAttr_write,              -4,  &bitop},\n    {\"getbit\",      QAttr_read,                3,  &getbit},\n    {\"setbit\",      QAttr_write,               4,  &setbit},\n    {\"incr\",        QAttr_write,               2,  &incr},\n    {\"decr\",        QAttr_write,               2,  &decr},\n    {\"incrby\",      QAttr_write,               3,  &incrby},\n    {\"incrbyfloat\", QAttr_write,               3,  &incrbyfloat},\n    {\"decrby\",      QAttr_write,               3,  &decrby},\n    {\"getrange\",    QAttr_read,                4,  &getrange},\n    {\"setrange\",    QAttr_write,               4,  &setrange},\n\n    // list\n    {\"lpush\",       QAttr_write,              -3,  &lpush},\n    {\"rpush\",       QAttr_write,              -3,  &rpush},\n    {\"lpushx\",      QAttr_write,              -3,  &lpushx},\n    {\"rpushx\",      QAttr_write,              -3,  &rpushx},\n    {\"lpop\",        QAttr_write,               2,  &lpop},\n    {\"rpop\",        QAttr_write,               2,  &rpop},\n    {\"lindex\",      QAttr_read,                3,  &lindex},\n    {\"llen\",        QAttr_read,                2,  &llen},\n    {\"lset\",        QAttr_write,               4,  &lset},\n    {\"ltrim\",       QAttr_write,               4,  &ltrim},\n    {\"lrange\",      QAttr_read,                4,  &lrange},\n    {\"linsert\",     QAttr_write,               5,  &linsert},\n    {\"lrem\",        QAttr_write,               4,  &lrem},\n    {\"rpoplpush\",   QAttr_write,               3,  &rpoplpush},\n    {\"blpop\",       QAttr_write,              -3,  &blpop},\n    {\"brpop\",       QAttr_write,              -3,  &brpop},\n    {\"brpoplpush\",  QAttr_write,               4,  &brpoplpush},\n\n    // hash\n    {\"hget\",        QAttr_read,                3,  &hget},\n    {\"hgetall\",     QAttr_read,                2,  &hgetall},\n    {\"hmget\",       QAttr_read,               -3,  &hmget},\n    {\"hset\",        QAttr_write,               4,  &hset},\n    {\"hsetnx\",      QAttr_write,               4,  &hsetnx},\n    {\"hmset\",       QAttr_write,              -4,  &hmset},\n    {\"hlen\",        QAttr_read,                2,  &hlen},\n    {\"hexists\",     QAttr_read,                3,  &hexists},\n    {\"hkeys\",       QAttr_read,                2,  &hkeys},\n    {\"hvals\",       QAttr_read,                2,  &hvals},\n    {\"hdel\",        QAttr_write,              -3,  &hdel},\n    {\"hincrby\",     QAttr_write,               4,  &hincrby},\n    {\"hincrbyfloat\",QAttr_write,               4,  &hincrbyfloat},\n    {\"hscan\",       QAttr_read,               -3,  &hscan},\n    {\"hstrlen\",     QAttr_read,                3,  &hstrlen},\n\n    // set\n    {\"sadd\",        QAttr_write,              -3,  &sadd},\n    {\"scard\",       QAttr_read,                2,  &scard},\n    {\"sismember\",   QAttr_read,                3,  &sismember},\n    {\"srem\",        QAttr_write,              -3,  &srem},\n    {\"smembers\",    QAttr_read,                2,  &smembers},\n    {\"sdiff\",       QAttr_read,               -2,  &sdiff},\n    {\"sdiffstore\",  QAttr_write,              -3,  &sdiffstore},\n    {\"sinter\",      QAttr_read,               -2,  &sinter},\n    {\"sinterstore\", QAttr_write,              -3,  &sinterstore},\n    {\"sunion\",      QAttr_read,               -2,  &sunion},\n    {\"sunionstore\", QAttr_write,              -3,  &sunionstore},\n    {\"smove\",       QAttr_write,               4,  &smove},\n    {\"spop\",        QAttr_write,               2,  &spop},\n    {\"srandmember\", QAttr_read,                2,  &srandmember},\n    {\"sscan\",       QAttr_read,               -3,  &sscan},\n\n    //\n    {\"zadd\",        QAttr_write,              -4,  &zadd},\n    {\"zcard\",       QAttr_read,                2,  &zcard},\n    {\"zrank\",       QAttr_read,                3,  &zrank},\n    {\"zrevrank\",    QAttr_read,                3,  &zrevrank},\n    {\"zrem\",        QAttr_write,              -3,  &zrem},\n    {\"zincrby\",     QAttr_write,               4,  &zincrby},\n    {\"zscore\",      QAttr_read,                3,  &zscore},\n    {\"zrange\",      QAttr_read,               -4,  &zrange},\n    {\"zrevrange\",   QAttr_read,               -4,  &zrevrange},\n    {\"zrangebyscore\",   QAttr_read,           -4,  &zrangebyscore},\n    {\"zrevrangebyscore\",QAttr_read,           -4,  &zrevrangebyscore},\n    {\"zremrangebyrank\", QAttr_write,           4,  &zremrangebyrank},\n    {\"zremrangebyscore\",QAttr_write,           4,  &zremrangebyscore},\n\n    // pubsub\n    {\"subscribe\",   QAttr_read,               -2,  &subscribe},\n    {\"unsubscribe\", QAttr_read,               -1,  &unsubscribe},\n    {\"publish\",     QAttr_read,                3,  &publish},\n    {\"psubscribe\",  QAttr_read,               -2,  &psubscribe},\n    {\"punsubscribe\",QAttr_read,               -1,  &punsubscribe},\n    {\"pubsub\",      QAttr_read,               -2,  &pubsub},\n    \n    \n    // multi\n    {\"watch\",       QAttr_read,               -2,  &watch},\n    {\"unwatch\",     QAttr_read,                1,  &unwatch},\n    {\"multi\",       QAttr_read,                1,  &multi},\n    {\"exec\",        QAttr_read,                1,  &exec},\n    {\"discard\",     QAttr_read,                1,  &discard},\n    \n    // replication\n    {\"sync\",        QAttr_read,                1,  &sync},\n    {\"psync\",       QAttr_read,                1,  &sync},\n    {\"slaveof\",     QAttr_read,                3,  &slaveof},\n    {\"replconf\",    QAttr_read,               -3,  &replconf},\n\n    // modules\n    {\"module\",      QAttr_read,               -2,  &module},\n   \n    // help\n    {\"cmdlist\",     QAttr_read,                1,  &cmdlist},\n};\n    \nDelegate<void (UnboundedBuffer& )> g_infoCollector;\n\nstd::map<QString, const QCommandInfo*, NocaseComp>  QCommandTable::s_handlers;\n\nQCommandTable::QCommandTable()\n{\n    Init();\n}\n\nvoid QCommandTable::Init()\n{\n    for (const auto& info : s_info)\n    {\n        s_handlers[info.cmd] = &info;\n    }\n    \n    g_infoCollector += OnMemoryInfoCollect;\n    g_infoCollector += OnServerInfoCollect;\n    g_infoCollector += OnClientInfoCollect;\n    g_infoCollector += std::bind(&QReplication::OnInfoCommand, &QREPL, std::placeholders::_1);\n}\n\nconst QCommandInfo* QCommandTable::GetCommandInfo(const QString& cmd)\n{\n    auto it(s_handlers.find(cmd));\n    if (it != s_handlers.end())\n    {\n        return it->second;\n    }\n    \n    return 0;\n}\n    \nbool  QCommandTable::AliasCommand(const std::map<QString, QString>& aliases)\n{\n    for (const auto& pair : aliases)\n    {\n        if (!AliasCommand(pair.first, pair.second))\n            return false;\n    }\n            \n    return true;\n}\n    \nbool  QCommandTable::AliasCommand(const QString& oldKey, const QString& newKey)\n{\n    auto info = DelCommand(oldKey);\n    if (!info)\n        return false;\n\n    return AddCommand(newKey, info);\n}\n\nconst QCommandInfo* QCommandTable::DelCommand(const QString& cmd)\n{\n    auto it(s_handlers.find(cmd));\n    if (it != s_handlers.end())\n    {\n        auto p = it->second;\n        s_handlers.erase(it);\n        return p;\n    }\n\n    return nullptr;\n}\n\nbool  QCommandTable::AddCommand(const QString& cmd, const QCommandInfo* info)\n{\n    if (cmd.empty() || cmd == \"\\\"\\\"\")\n        return true;\n\n    return s_handlers.insert(std::make_pair(cmd, info)).second;\n}\n\nQError QCommandTable::ExecuteCmd(const std::vector<QString>& params, const QCommandInfo* info, UnboundedBuffer* reply)\n{\n    if (params.empty())\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n\n    if (!info)\n    {\n        ReplyError(QError_unknowCmd, reply);\n        return QError_unknowCmd;\n    }\n    \n    if (!info->CheckParamsCount(static_cast<int>(params.size())))\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n\n    return info->handler(params, reply);\n}\n\nQError QCommandTable::ExecuteCmd(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.empty())\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    auto it(s_handlers.find(params[0]));\n    if (it == s_handlers.end())\n    {\n        ReplyError(QError_unknowCmd, reply);\n        return QError_unknowCmd;\n    }\n    \n    const QCommandInfo* info = it->second;\n    if (!info->CheckParamsCount(static_cast<int>(params.size())))\n    {\n        ReplyError(QError_param, reply);\n        return   QError_param;\n    }\n    \n    return info->handler(params, reply);\n}\n\n    \nbool QCommandInfo::CheckParamsCount(int nParams) const\n{\n    if (params > 0)\n        return params == nParams;\n    else\n        return nParams + params >= 0;\n}\n\nQError cmdlist(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    PreFormatMultiBulk(QCommandTable::s_handlers.size(), reply);\n    for (const auto& kv : QCommandTable::s_handlers)\n    {\n        FormatBulk(kv.first, reply);\n    }\n\n    return QError_ok;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QCommand.h",
    "content": "#ifndef BERT_QCOMMAND_H\n#define BERT_QCOMMAND_H\n\n#include <vector>\n#include <map>\n#include \"QCommon.h\"\n#include \"QString.h\"\n#include \"Delegate.h\"\n\nnamespace qedis\n{\n\nenum QCommandAttr\n{\n    QAttr_read  = 0x1,\n    QAttr_write = 0x1 << 1,\n};\n\n\nclass UnboundedBuffer;\nusing QCommandHandler = QError (const std::vector<QString>& params, UnboundedBuffer* reply);\n\n// key commands\nQCommandHandler  type;\nQCommandHandler  exists;\nQCommandHandler  del;\nQCommandHandler  expire;\nQCommandHandler  pexpire;\nQCommandHandler  expireat;\nQCommandHandler  pexpireat;\nQCommandHandler  ttl;\nQCommandHandler  pttl;\nQCommandHandler  persist;\nQCommandHandler  move;\nQCommandHandler  keys;\nQCommandHandler  randomkey;\nQCommandHandler  rename;\nQCommandHandler  renamenx;\nQCommandHandler  scan;\nQCommandHandler  sort;\nQCommandHandler  dump;\nQCommandHandler  restore;\nQCommandHandler  migrate;\n\n// server commands\nQCommandHandler  select;\nQCommandHandler  dbsize;\nQCommandHandler  bgsave;\nQCommandHandler  save;\nQCommandHandler  lastsave;\nQCommandHandler  flushdb;\nQCommandHandler  flushall;\nQCommandHandler  client;\nQCommandHandler  debug;\nQCommandHandler  shutdown;\nQCommandHandler  bgrewriteaof;\nQCommandHandler  ping;\nQCommandHandler  echo;\nQCommandHandler  info;\nQCommandHandler  monitor;\nQCommandHandler  auth;\nQCommandHandler  slowlog;\nQCommandHandler  config;\n\n// string commands\nQCommandHandler  set;\nQCommandHandler  get;\nQCommandHandler  getrange;\nQCommandHandler  setrange;\nQCommandHandler  getset;\nQCommandHandler  append;\nQCommandHandler  bitcount;\nQCommandHandler  bitop;\nQCommandHandler  getbit;\nQCommandHandler  setbit;\nQCommandHandler  incr;\nQCommandHandler  incrby;\nQCommandHandler  incrbyfloat;\nQCommandHandler  decr;\nQCommandHandler  decrby;\nQCommandHandler  mget;\nQCommandHandler  mset;\nQCommandHandler  msetnx;\nQCommandHandler  setnx;\nQCommandHandler  setex;\nQCommandHandler  psetex;\nQCommandHandler  strlen;\n\n// list commands\nQCommandHandler  lpush;\nQCommandHandler  rpush;\nQCommandHandler  lpushx;\nQCommandHandler  rpushx;\nQCommandHandler  lpop;\nQCommandHandler  rpop;\nQCommandHandler  lindex;\nQCommandHandler  llen;\nQCommandHandler  lset;\nQCommandHandler  ltrim;\nQCommandHandler  lrange;\nQCommandHandler  linsert;\nQCommandHandler  lrem;\nQCommandHandler  rpoplpush;\nQCommandHandler  blpop;\nQCommandHandler  brpop;\nQCommandHandler  brpoplpush;\n\n// hash commands\nQCommandHandler  hget;\nQCommandHandler  hmget;\nQCommandHandler  hgetall;\nQCommandHandler  hset;\nQCommandHandler  hsetnx;\nQCommandHandler  hmset;\nQCommandHandler  hlen;\nQCommandHandler  hexists;\nQCommandHandler  hkeys;\nQCommandHandler  hvals;\nQCommandHandler  hdel;\nQCommandHandler  hincrby;\nQCommandHandler  hincrbyfloat;\nQCommandHandler  hscan;\nQCommandHandler  hstrlen;\n\n// set commands\nQCommandHandler  sadd;\nQCommandHandler  scard;\nQCommandHandler  srem;\nQCommandHandler  sismember;\nQCommandHandler  smembers;\nQCommandHandler  sdiff;\nQCommandHandler  sdiffstore;\nQCommandHandler  sinter;\nQCommandHandler  sinterstore;\nQCommandHandler  sunion;\nQCommandHandler  sunionstore;\nQCommandHandler  smove;\nQCommandHandler  spop;\nQCommandHandler  srandmember;\nQCommandHandler  sscan;\n\n\n// sset\nQCommandHandler  zadd;\nQCommandHandler  zcard;\nQCommandHandler  zrank;\nQCommandHandler  zrevrank;\nQCommandHandler  zrem;\nQCommandHandler  zincrby;\nQCommandHandler  zscore;\nQCommandHandler  zrange;\nQCommandHandler  zrevrange;\nQCommandHandler  zrangebyscore;\nQCommandHandler  zrevrangebyscore;\nQCommandHandler  zremrangebyrank;\nQCommandHandler  zremrangebyscore;\n\n// pubsub\nQCommandHandler  subscribe;\nQCommandHandler  unsubscribe;\nQCommandHandler  publish;\nQCommandHandler  psubscribe;\nQCommandHandler  punsubscribe;\nQCommandHandler  pubsub;\n\n//multi\nQCommandHandler  watch;\nQCommandHandler  unwatch;\nQCommandHandler  multi;\nQCommandHandler  exec;\nQCommandHandler  discard;\n\n// replication\nQCommandHandler  sync;\nQCommandHandler  slaveof;\nQCommandHandler  replconf;\n\n// modules\nQCommandHandler  module;\n    \n// help\nQCommandHandler  cmdlist;\n\n\nextern Delegate<void (UnboundedBuffer& )> g_infoCollector;\nextern void OnMemoryInfoCollect(UnboundedBuffer& );\nextern void OnServerInfoCollect(UnboundedBuffer& );\nextern void OnClientInfoCollect(UnboundedBuffer& );\n\nstruct QCommandInfo\n{\n    QString     cmd;\n    int         attr;\n    int         params;\n    QCommandHandler* handler;\n    bool  CheckParamsCount(int nParams) const;\n};\n\nclass QCommandTable\n{\npublic:\n    QCommandTable();\n    \n    static void Init();\n\n    static const QCommandInfo* GetCommandInfo(const QString& cmd);\n    static QError ExecuteCmd(const std::vector<QString>& params, const QCommandInfo* info, UnboundedBuffer* reply = nullptr);\n    static QError ExecuteCmd(const std::vector<QString>& params, UnboundedBuffer* reply = nullptr);\n\n    static bool  AliasCommand(const std::map<QString, QString>& aliases);\n    static bool  AliasCommand(const QString& oldKey, const QString& newKey);\n\n    static bool  AddCommand(const QString& cmd, const QCommandInfo* info);\n    static const QCommandInfo* DelCommand(const QString& cmd);\n\n    friend QCommandHandler cmdlist;\nprivate:\n\n    static const QCommandInfo s_info[];\n\n    static std::map<QString, const QCommandInfo*, NocaseComp>  s_handlers;\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QCommon.cc",
    "content": "#include \"QCommon.h\"\n#include \"UnboundedBuffer.h\"\n#include <limits>\n#include <stdlib.h>\n#include <errno.h>\n#include <algorithm>\n#include <iostream>\n\nnamespace qedis\n{\n\nstruct QErrorInfo g_errorInfo[] = {\n    {sizeof \"+OK\\r\\n\"- 1,   \"+OK\\r\\n\"},\n    {sizeof \"-ERR Operation against a key holding the wrong kind of value\\r\\n\"- 1, \"-ERR Operation against a key holding the wrong kind of value\\r\\n\"}, \n    {sizeof \"-ERR already exist\"- 1,   \"-ERR already exist\"},\n    {sizeof \"-ERR no such key\\r\\n\" - 1, \"-ERR no such key\\r\\n\"},\n    {sizeof \"-ERR wrong number of arguments\\r\\n\"- 1, \"-ERR wrong number of arguments\\r\\n\"},\n    {sizeof \"-ERR Unknown command\\r\\n\"- 1,   \"-ERR Unknown command\\r\\n\"},\n    {sizeof \"-ERR value is not an integer or out of range\\r\\n\"- 1, \"-ERR value is not an integer or out of range\\r\\n\"},\n    {sizeof \"-ERR syntax error\\r\\n\"-1, \"-ERR syntax error\\r\\n\"},\n    \n    {sizeof \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"-1, \"-EXECABORT Transaction discarded because of previous errors.\\r\\n\"},\n    {sizeof \"-WATCH inside MULTI is not allowed\\r\\n\"-1, \"-WATCH inside MULTI is not allowed\\r\\n\"},\n    {sizeof \"-EXEC without MULTI\\r\\n\"-1, \"-EXEC without MULTI\\r\\n\"},\n    {sizeof \"-ERR invalid DB index\\r\\n\"-1, \"-ERR invalid DB index\\r\\n\"},\n    {sizeof \"-READONLY You can't write against a read only slave.\\r\\n\"-1, \"-READONLY You can't write against a read only slave.\\r\\n\"},\n    {sizeof \"-ERR operation not permitted\\r\\n\"-1, \"-ERR operation not permitted\\r\\n\"},\n    {sizeof \"-ERR invalid password\\r\\n\"-1, \"-ERR invalid password\\r\\n\"},\n    {sizeof \"-ERR no such module\\r\\n\"-1, \"-ERR no such module\\r\\n\"},\n    {sizeof \"-ERR init module failed\\r\\n\"-1, \"-ERR init module failed\\r\\n\"},\n    {sizeof \"-ERR uninit module failed\\r\\n\"-1, \"-ERR uninit module failed\\r\\n\"},\n    {sizeof \"-ERR module already loaded\\r\\n\"-1, \"-ERR module already loaded\\r\\n\"},\n    {sizeof \"-BUSYKEY Target key name already exists.\\r\\n\"-1, \"-BUSYKEY Target key name already exists.\\r\\n\"},\n    //\n};\n\n\nint Double2Str(char* ptr, std::size_t nBytes, double val)\n{\n    return snprintf(ptr, nBytes - 1, \"%.6g\", val);\n}\n\nbool TryStr2Long(const char* ptr, size_t nBytes, long& val)\n{\n    bool negtive = false;\n    size_t  i = 0;\n\n    if (ptr[0] == '-' || ptr[0] == '+')\n    {\n        if (nBytes <= 1 || !isdigit(ptr[1]))\n            return false;\n\n        negtive = (ptr[0] == '-');\n        i = 1;\n    }\n\n    val = 0;\n    for (; i < nBytes; ++ i)\n    {\n        if (!isdigit(ptr[i]))\n            break;\n        \n        if (!negtive && val > std::numeric_limits<long>::max() / 10)\n        {\n            std::cerr << \"long will overflow \" << val << std::endl;\n            return false;\n        }\n        \n        if (negtive && val > (-(std::numeric_limits<long>::min() + 1)) / 10)\n        {\n            std::cerr << \"long will underflow \" << val << std::endl;\n            return false;\n        }\n\n        val *= 10;\n        \n        if (!negtive && val > std::numeric_limits<long>::max() - ( ptr[i] - '0'))\n        {\n            std::cerr << \"long will overflow \" << val << std::endl;\n            return false;\n        }\n        \n        \n        if (negtive && (val - 1) > (-(std::numeric_limits<long>::min() + 1)) - (ptr[i] - '0'))\n        {\n            std::cerr << \"long will underflow \" << val << std::endl;\n            return false;\n        }\n        \n        val += ptr[i] - '0';\n    }\n\n    if (negtive)\n    {\n        val *= -1;\n    }\n\n    return true;\n}\n\nbool Strtol(const char* ptr, size_t nBytes, long* outVal)\n{\n    if (nBytes == 0 || nBytes > 20) // include the sign\n        return false;\n\n    errno = 0;\n    char* pEnd = 0;\n    *outVal = strtol(ptr, &pEnd, 0);\n\n    if (errno == ERANGE ||\n        errno == EINVAL)\n        return false;\n\n    return pEnd == ptr + nBytes;\n}\n\nbool Strtoll(const char* ptr, size_t nBytes, long long* outVal)\n{\n    if (nBytes == 0 || nBytes > 20)\n        return false;\n    \n    errno  = 0;\n    char* pEnd = 0;\n    *outVal = strtoll(ptr, &pEnd, 0);\n    \n    if (errno == ERANGE ||\n        errno == EINVAL)\n        return false;\n    \n    return pEnd == ptr + nBytes;\n}\n\nbool Strtof(const char* ptr, size_t nBytes, float* outVal)\n{\n    if (nBytes == 0 || nBytes > 20)\n        return false;\n    \n    errno = 0;\n    char* pEnd = 0;\n    *outVal = strtof(ptr, &pEnd);\n    \n    if (errno == ERANGE ||\n        errno == EINVAL)\n        return false;\n    \n    return pEnd == ptr + nBytes;\n}\n\nbool Strtod(const char* ptr, size_t nBytes, double* outVal)\n{\n    if (nBytes == 0 || nBytes > 20)\n        return false;\n    \n    errno = 0;\n    char* pEnd = 0;\n    *outVal = strtod(ptr, &pEnd);\n    \n    if (errno == ERANGE ||\n        errno == EINVAL)\n        return false;\n    \n    return pEnd == ptr + nBytes;\n}\n\n\nconst char* Strstr(const char* ptr, size_t nBytes, const char* pattern, size_t nBytes2)\n{\n    if (!pattern || *pattern == 0)\n        return 0;\n    \n    const char* ret = std::search(ptr, ptr + nBytes, pattern, pattern + nBytes2);\n    return  ret == ptr + nBytes ? 0 : ret;\n}\n\nconst char* SearchCRLF(const char* ptr, size_t nBytes)\n{\n    return  Strstr(ptr, nBytes, CRLF, 2);\n}\n\nsize_t  FormatInt(long value, UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    char val[32];\n    int len = snprintf(val, sizeof val, \"%ld\" CRLF, value);\n    \n    size_t  oldSize = reply->ReadableSize();\n    reply->PushData(\":\", 1);\n    reply->PushData(val, len);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t  FormatSingle(const char* str, size_t len, UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    size_t   oldSize = reply->ReadableSize();\n    reply->PushData(\"+\", 1);\n    reply->PushData(str, len);\n    reply->PushData(CRLF, 2);\n\n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t  FormatSingle(const QString& str, UnboundedBuffer* reply)\n{\n    return  FormatSingle(str.c_str(), str.size(), reply);\n}\n\nsize_t  FormatBulk(const char* str, size_t len, UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    size_t oldSize = reply->ReadableSize();\n    reply->PushData(\"$\", 1);\n\n    char val[32];\n    int tmp = snprintf(val, sizeof val - 1, \"%lu\" CRLF, len);\n    reply->PushData(val, tmp);\n\n    if (str && len > 0)\n    {\n        reply->PushData(str, len);\n    }\n    \n    reply->PushData(CRLF, 2);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t  FormatBulk(const QString& str, UnboundedBuffer* reply)\n{\n    return  FormatBulk(str.c_str(), str.size(), reply);\n}\n\nsize_t  PreFormatMultiBulk(size_t nBulk, UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    size_t  oldSize = reply->ReadableSize();\n    reply->PushData(\"*\", 1);\n\n    char val[32];\n    int tmp = snprintf(val, sizeof val - 1, \"%lu\" CRLF, nBulk);\n    reply->PushData(val, tmp);\n\n    return reply->ReadableSize() - oldSize;\n}\n\nstd::size_t FormatMultiBulk(const std::vector<QString> vs, UnboundedBuffer* reply)\n{\n    size_t size = 0;\n    for (const auto& s : vs)\n        size += FormatBulk(s, reply);\n\n    return size;\n}\n\nstd::size_t FormatEmptyBulk(UnboundedBuffer* reply)\n{\n    return reply->PushData(\"$0\" CRLF CRLF, 6);\n}\n\nvoid ReplyError(QError err, UnboundedBuffer* reply)\n{\n    if (!reply)\n        return;\n    \n    const QErrorInfo& info = g_errorInfo[err];\n\n    reply->PushData(info.errorStr, info.len);\n}\n\nsize_t FormatNull(UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    size_t   oldSize = reply->ReadableSize();\n    reply->PushData(\"$-1\" CRLF, 5);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\n\nsize_t  FormatNullArray(UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    size_t oldSize = reply->ReadableSize();\n    reply->PushData(\"*-1\" CRLF, 5);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t FormatOK(UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    size_t oldSize = reply->ReadableSize();\n    reply->PushData(\"+OK\" CRLF, 5);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t Format1(UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n    \n    const char* val = \":1\\r\\n\";\n    \n    size_t oldSize = reply->ReadableSize();\n    reply->PushData(val, 4);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nsize_t Format0(UnboundedBuffer* reply)\n{\n    if (!reply)\n        return 0;\n\n    const char* val = \":0\\r\\n\";\n    \n    size_t oldSize = reply->ReadableSize();\n    reply->PushData(val, 4);\n    \n    return reply->ReadableSize() - oldSize;\n}\n\nQParseResult GetIntUntilCRLF(const char*& ptr, std::size_t nBytes, int& val)\n{\n    if (nBytes < 3)\n        return QParseResult::wait;\n    \n    std::size_t i = 0;\n    bool negtive = false;\n    if (ptr[0] == '-')\n    {\n        negtive = true;\n        ++ i;\n    }\n    else if (ptr[0] == '+')\n    {\n        ++ i;\n    }\n    \n    int value = 0;\n    for (; i < nBytes; ++ i)\n    {\n        if (isdigit(ptr[i]))\n        {\n            value *= 10;\n            value += ptr[i] - '0';\n        }\n        else\n        {\n            if (ptr[i] != '\\r' || (i+1 < nBytes && ptr[i+1] != '\\n'))\n                return QParseResult::error;\n            \n            if (i + 1 == nBytes)\n                return QParseResult::wait;\n            \n            break;\n        }\n    }\n    \n    if (negtive)\n        value *= -1;\n    \n    ptr += i;\n    ptr += 2;\n    val = value;\n    return QParseResult::ok;\n}\n\nstd::vector<QString>  SplitString(const QString& str, char seperator)\n{\n    std::vector<QString>  results;\n    \n    QString::size_type start = 0;\n    QString::size_type sep = str.find(seperator);\n    while (sep != QString::npos)\n    {\n        if (start < sep)\n            results.emplace_back(str.substr(start, sep - start));\n        \n        start = sep + 1;\n        sep   = str.find(seperator, start);\n    }\n    \n    if (start != str.size())\n        results.emplace_back(str.substr(start));\n    \n    return results;\n}\n    \n}\n"
  },
  {
    "path": "QedisCore/QCommon.h",
    "content": "#ifndef BERT_QCOMMON_H\n#define BERT_QCOMMON_H\n\n#include <cstddef>\n#include <stdio.h>\n#include <vector>\n#include <algorithm>\n#include <functional>\n#include <strings.h>\n#include \"QString.h\"\n\n#define QEDIS static_cast<Qedis* >(Server::Instance())\n\n#define CRLF \"\\r\\n\"\n\n\nnamespace qedis\n{\n\nconst int kStringMaxBytes = 1 * 1024 * 1024 * 1024;\n    \nenum QType\n{\n    QType_invalid,\n    QType_string,\n    QType_list,\n    QType_set,\n    QType_sortedSet,\n    QType_hash,\n    // < 16\n};\n\nenum QEncode\n{\n    QEncode_invalid,\n\n    QEncode_raw, // string\n    QEncode_int, // string as int\n\n    QEncode_list,\n    \n    QEncode_set,\n    QEncode_hash,\n    \n    QEncode_sset,\n};\n\ninline const char* EncodingStringInfo(unsigned encode)\n{\n    switch (encode)\n    {\n        case QEncode_raw:\n            return \"raw\";\n            \n        case QEncode_int:\n            return \"int\";\n            \n        case QEncode_list:\n            return \"list\";\n            \n        case QEncode_set:\n            return \"set\";\n            \n        case QEncode_hash:\n            return \"hash\";\n            \n        case QEncode_sset:\n            return \"sset\";\n            \n        default:\n            break;\n    }\n    \n    return \"unknown\";\n}\n\nenum QError\n{\n    QError_nop       = -1,\n    QError_ok        = 0,\n    QError_type      = 1,\n    QError_exist     = 2,\n    QError_notExist  = 3,\n    QError_param     = 4,\n    QError_unknowCmd = 5,\n    QError_nan       = 6,\n    QError_syntax    = 7,\n    QError_dirtyExec = 8,\n    QError_watch     = 9,\n    QError_noMulti   = 10,\n    QError_invalidDB = 11,\n    QError_readonlySlave = 12,\n    QError_needAuth  = 13,\n    QError_errAuth   = 14,\n    QError_nomodule   = 15,\n    QError_moduleinit = 16,\n    QError_moduleuninit = 17,\n    QError_modulerepeat = 18,\n    QError_busykey      = 19,\n    QError_max,\n};\n\nextern struct QErrorInfo\n{\n    int len;\n    const char* errorStr;\n} g_errorInfo[] ;\n\ntemplate <typename T>\ninline std::size_t Number2Str(char* ptr, std::size_t nBytes, T val)\n{\n    if (!ptr || nBytes < 2)\n        return 0;\n\n    if (val == 0)\n    {\n        ptr[0] = '0';\n        ptr[1] = 0;\n        return 1;\n    }\n\n    bool negative = false;\n    if (val < 0)\n    {\n        negative = true;\n        val = -val;\n    }\n\n    std::size_t off = 0;\n    while (val > 0)\n    {\n        if (off >= nBytes)\n            return 0;\n\n        ptr[off ++] = val % 10 + '0';\n        val /= 10;\n    }\n\n    if (negative)\n    {\n        if (off >= nBytes)\n            return 0;\n\n        ptr[off ++] = '-';\n    }\n\n    std::reverse(ptr, ptr + off);\n    ptr[off] = 0;\n\n    return off;\n}\n\nint         Double2Str(char* ptr, std::size_t nBytes, double val);\nbool        TryStr2Long(const char* ptr, std::size_t nBytes, long& val); // only for decimal\nbool        Strtol(const char* ptr, std::size_t nBytes, long* outVal);\nbool        Strtoll(const char* ptr, std::size_t nBytes, long long* outVal);\nbool        Strtof(const char* ptr, std::size_t nBytes, float* outVal);\nbool        Strtod(const char* ptr, std::size_t nBytes, double* outVal);\nconst char* Strstr(const char* ptr, std::size_t nBytes, const char* pattern, std::size_t nBytes2);\nconst char* SearchCRLF(const char* ptr, std::size_t nBytes);\n\n\nclass UnboundedBuffer;\n\nstd::size_t FormatInt(long value, UnboundedBuffer* reply);\nstd::size_t FormatSingle(const char* str, std::size_t len, UnboundedBuffer* reply);\nstd::size_t FormatSingle(const QString& str, UnboundedBuffer* reply);\nstd::size_t FormatBulk(const char* str, std::size_t len, UnboundedBuffer* reply);\nstd::size_t FormatBulk(const QString& str, UnboundedBuffer* reply);\nstd::size_t PreFormatMultiBulk(std::size_t nBulk, UnboundedBuffer* reply);\nstd::size_t FormatMultiBulk(const std::vector<QString> vs, UnboundedBuffer* reply);\n\nstd::size_t FormatEmptyBulk(UnboundedBuffer* reply);\nstd::size_t FormatNull(UnboundedBuffer* reply);\nstd::size_t FormatNullArray(UnboundedBuffer* reply);\nstd::size_t FormatOK(UnboundedBuffer* reply);\nstd::size_t Format1(UnboundedBuffer* reply);\nstd::size_t Format0(UnboundedBuffer* reply);\n\nvoid  ReplyError(QError err, UnboundedBuffer* reply);\n\ninline void AdjustIndex(long& start, long& end, size_t  size)\n{\n    if (size == 0)\n    {\n        end = 0, start = 1;\n        return;\n    }\n    \n    if (start < 0)  start += size;\n    if (start < 0)  start = 0;\n    if (end < 0)    end += size;\n    \n    if (end >= static_cast<long>(size))  end = size - 1;\n}\n\nstruct NocaseComp\n{\n    bool operator() (const QString& s1, const QString& s2) const\n    {\n        return strcasecmp(s1.c_str(), s2.c_str()) < 0;\n    }\n\n    bool operator() (const char* s1, const QString& s2) const\n    {\n        return strcasecmp(s1, s2.c_str()) < 0;\n    }\n\n    bool operator() (const QString& s1, const char* s2) const\n    {\n        return strcasecmp(s1.c_str(), s2) < 0;\n    }\n};\n\nenum class QParseResult : int8_t\n{\n    ok,\n    wait,\n    error,\n};\n\nQParseResult GetIntUntilCRLF(const char*& ptr, std::size_t nBytes, int& val);\n\nstd::vector<QString> SplitString(const QString& str, char seperator);\n\n// Build redis request from multiple strings, use inline protocol \ntemplate <typename... Args>\nstd::string BuildInlineRequest(Args&& ...);\n\ntemplate <typename S>\ninline\nstd::string BuildInlineRequest(S&& s)\n{\n    return std::string(std::forward<S>(s)) + \"\\r\\n\";\n}\n\ntemplate <typename H, typename... T>\ninline\nstd::string BuildInlineRequest(H&& head, T&&... tails)\n{\n    std::string h(std::forward<H>(head));\n    return h + \" \" + BuildInlineRequest(std::forward<T>(tails)...);\n}\n\n    \n// The defer class for C++11\nclass ExecuteOnScopeExit\n{\npublic:\n    ExecuteOnScopeExit() { }\n    \n    ExecuteOnScopeExit(ExecuteOnScopeExit&& e)\n    {\n        func_ = std::move(e.func_);\n    }\n    \n    ExecuteOnScopeExit(const ExecuteOnScopeExit& e) = delete;\n    void operator=(const ExecuteOnScopeExit& f) = delete;\n    \n    template <typename F, typename... Args>\n    ExecuteOnScopeExit(F&& f, Args&&... args)\n    {\n        auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n        func_ = [temp]() { (void)temp(); };\n    }\n    \n    ~ExecuteOnScopeExit() noexcept\n    {\n        if (func_)  func_();\n    }\n    \nprivate:\n    std::function<void ()> func_;\n};\n    \n#define CONCAT(a, b) a##b\n#define _MAKE_DEFER_HELPER_(line)  qedis::ExecuteOnScopeExit CONCAT(defer, line) = [&]()\n\n\n#define QEDIS_DEFER _MAKE_DEFER_HELPER_(__LINE__)\n\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QConfig.cc",
    "content": "#include <vector>\n#include <iostream>\n\n#include \"QConfig.h\"\n#include \"ConfigParser.h\"\n\nnamespace qedis\n{\n\nstatic void EraseQuotes(QString& str)\n{\n    // convert \"hello\" to  hello\n    if (str.size() < 2)\n        return;\n    if (str[0] == '\"' && str[str.size() - 1] == '\"')\n    {\n        str.erase(str.begin());\n        str.pop_back();\n    }\n}\n\nextern std::vector<QString>  SplitString(const QString& str, char seperator);\n\nQConfig  g_config;\n\nQConfig::QConfig()\n{\n    daemonize  = false;\n    pidfile = \"/var/run/qedis.pid\";\n    \n    ip = \"127.0.0.1\";\n    port = 6379;\n    timeout = 0;\n    \n    loglevel = \"notice\";\n    logdir = \"stdout\";\n    \n    databases = 16;\n    \n    // rdb\n    saveseconds = 999999999;\n    savechanges = 999999999;\n    rdbcompression = true;\n    rdbchecksum    = true;\n    rdbfullname    = \"./dump.rdb\";\n    \n    maxclients = 10000;\n    \n    // aof\n    appendonly = false;\n    appendfilename = \"appendonly.aof\";\n    appendfsync = 0;\n    \n    // slow log\n    slowlogtime = 0;\n    slowlogmaxlen = 128;\n    \n    hz = 10;\n    \n    includefile = \"\";\n\n    maxmemory = 2 * 1024 * 1024 * 1024UL;\n    maxmemorySamples = 5;\n    noeviction = true;\n\n    backend = BackEndNone;\n    backendPath = \"dump\";\n    backendHz = 10;\n}\n\nbool  LoadQedisConfig(const char* cfgFile, QConfig& cfg)\n{\n    ConfigParser  parser;\n    if (!parser.Load(cfgFile))\n        return false;\n    \n    if (parser.GetData<QString>(\"daemonize\") == \"yes\")\n        cfg.daemonize = true;\n    else\n        cfg.daemonize = false;\n    \n    cfg.pidfile = parser.GetData<QString>(\"pidfile\", cfg.pidfile);\n    \n    cfg.ip      = parser.GetData<QString>(\"bind\", cfg.ip);\n    cfg.port    = parser.GetData<unsigned short>(\"port\");\n    cfg.timeout = parser.GetData<int>(\"timeout\");\n    \n    cfg.loglevel = parser.GetData<QString>(\"loglevel\", cfg.loglevel);\n    cfg.logdir   = parser.GetData<QString>(\"logfile\", cfg.logdir);\n    EraseQuotes(cfg.logdir);\n    if (cfg.logdir.empty())\n        cfg.logdir = \"stdout\";\n    \n    cfg.databases = parser.GetData<int>(\"databases\", cfg.databases);\n    cfg.password  = parser.GetData<QString>(\"requirepass\");\n    EraseQuotes(cfg.password);\n\n    // alias command\n    {\n        std::vector<QString>  alias(SplitString(parser.GetData<QString>(\"rename-command\"), ' '));\n        if (alias.size() % 2 == 0)\n        {\n            for (auto it(alias.begin()); it != alias.end(); )\n            {\n                const QString& oldCmd =  *(it ++);\n                const QString& newCmd =  *(it ++);\n                cfg.aliases[oldCmd] = newCmd;\n            }\n        }\n    }\n    \n    // load rdb config\n    std::vector<QString>  saveInfo(SplitString(parser.GetData<QString>(\"save\"), ' '));\n    if (!saveInfo.empty() && saveInfo.size() != 2)\n    {\n        EraseQuotes(saveInfo[0]);\n        if (!(saveInfo.size() == 1 && saveInfo[0].empty()))\n        {\n            std::cerr << \"bad format save rdb interval, bad string \"\n                      << parser.GetData<QString>(\"save\")\n                      << std::endl;\n            return false;\n        }\n    }\n    else if (!saveInfo.empty())\n    {\n        cfg.saveseconds = std::stoi(saveInfo[0]);\n        cfg.savechanges = std::stoi(saveInfo[1]);\n    }\n    \n    if (cfg.saveseconds == 0)\n        cfg.saveseconds = 999999999;\n    if (cfg.savechanges == 0)\n        cfg.savechanges = 999999999;\n    \n    cfg.rdbcompression = (parser.GetData<QString>(\"rdbcompression\") == \"yes\");\n    cfg.rdbchecksum    = (parser.GetData<QString>(\"rdbchecksum\") == \"yes\");\n    \n    cfg.rdbfullname    = parser.GetData<QString>(\"dir\", \"./\") + \\\n                         parser.GetData<QString>(\"dbfilename\", \"dump.rdb\");\n    \n    cfg.maxclients = parser.GetData<int>(\"maxclients\", 10000);\n    cfg.appendonly = (parser.GetData<QString>(\"appendonly\", \"no\") == \"yes\");\n    cfg.appendfilename = parser.GetData<const char* >(\"appendfilename\", \"appendonly.aof\");\n    if (cfg.appendfilename.size() <= 2)\n        return false;\n\n    if (cfg.appendfilename[0] == '\"') // redis.conf use quote for string, but qedis do not. For compatiable...\n        cfg.appendfilename = cfg.appendfilename.substr(1, cfg.appendfilename.size() - 2);\n\n    QString tmpfsync = parser.GetData<const char* >(\"appendfsync\", \"no\");\n    // qedis always use \"always\", fsync is done in another thread\n    if (tmpfsync == \"everysec\")\n    {\n    }\n    else if (tmpfsync == \"always\")\n    {\n    }\n    else\n    {\n    }\n    \n    cfg.slowlogtime = parser.GetData<int>(\"slowlog-log-slower-than\", 0);\n    cfg.slowlogmaxlen = parser.GetData<int>(\"slowlog-max-len\", cfg.slowlogmaxlen);\n    \n    cfg.hz = parser.GetData<int>(\"hz\", 10);\n\n    // load master ip port\n    std::vector<QString>  master(SplitString(parser.GetData<QString>(\"slaveof\"), ' '));\n    if (master.size() == 2)\n    {\n        cfg.masterIp   = std::move(master[0]);\n        cfg.masterPort = static_cast<unsigned short>(std::stoi(master[1]));\n    }\n    cfg.masterauth = parser.GetData<QString>(\"masterauth\");\n\n    // load modules' names\n    cfg.modules = parser.GetDataVector(\"loadmodule\");\n    \n    cfg.includefile = parser.GetData<QString>(\"include\"); //TODO multi files include\n\n    // lru cache\n    cfg.maxmemory = parser.GetData<uint64_t>(\"maxmemory\", 2 * 1024 * 1024 * 1024UL);\n    cfg.maxmemorySamples = parser.GetData<int>(\"maxmemory-samples\", 5);\n    cfg.noeviction = (parser.GetData<QString>(\"maxmemory-policy\", \"noeviction\") == \"noeviction\");\n\n    cfg.backend = parser.GetData<int>(\"backend\", BackEndNone);\n    cfg.backendPath = parser.GetData<QString>(\"backendpath\", cfg.backendPath);\n    EraseQuotes(cfg.backendPath);\n    cfg.backendHz = parser.GetData<int>(\"backendhz\", 10);\n\n    // cluster\n    cfg.enableCluster = parser.GetData<QString>(\"cluster\", \"off\") == \"on\";\n    if (cfg.enableCluster)\n    {\n        cfg.centers = SplitString(parser.GetData<QString>(\"clustercenters\"), ';');\n        cfg.setid = parser.GetData<int>(\"setid\", -1);\n    }\n    \n    return  cfg.CheckArgs();\n}\n    \nbool  QConfig::CheckArgs() const\n{\n#define RETURN_IF_FAIL(cond)\\\n    if (!(cond)) { \\\n        std::cerr << #cond \" failed\\n\"; \\\n        return  false; \\\n    }\n    \n    RETURN_IF_FAIL(port > 0);\n    RETURN_IF_FAIL(databases > 0);\n    RETURN_IF_FAIL(maxclients > 0);\n    RETURN_IF_FAIL(hz > 0 && hz < 500);\n    RETURN_IF_FAIL(maxmemory >= 512 * 1024 * 1024UL);\n    RETURN_IF_FAIL(maxmemorySamples > 0 && maxmemorySamples < 10);\n    RETURN_IF_FAIL(backend >= BackEndNone && backend < BackEndMax);\n    RETURN_IF_FAIL(backendHz >= 1 && backendHz <= 50);\n\n    if (enableCluster)\n    {\n        RETURN_IF_FAIL(!centers.empty());\n        RETURN_IF_FAIL(setid >= 0);\n    }\n\n#undef RETURN_IF_FAIL\n    \n    return  true;\n}\n\nbool QConfig::CheckPassword(const QString& pwd) const \n{\n    return password.empty() || password == pwd;\n}\n    \n}\n"
  },
  {
    "path": "QedisCore/QConfig.h",
    "content": "#ifndef BERT_QCONFIG_H\n#define BERT_QCONFIG_H\n\n#include <map>\n#include <vector>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nenum BackEndType\n{\n    BackEndNone = 0,\n    BackEndLeveldb = 1,\n    BackEndMax = 2,\n};\n\nstruct QConfig\n{\n    bool      daemonize;\n    QString   pidfile;\n    \n    QString   ip;\n    unsigned short  port;\n    \n    int       timeout;\n    \n    QString   loglevel;\n    QString   logdir;  // the log directory, differ from redis\n    \n    int       databases;\n    \n    // auth\n    QString   password;\n    \n    std::map<QString, QString>   aliases;\n\n    // @ rdb\n    // save seconds changes\n    int       saveseconds;\n    int       savechanges;\n    bool      rdbcompression;   // yes\n    bool      rdbchecksum;      // yes\n    QString   rdbfullname;      // ./dump.rdb\n    \n    int       maxclients;       // 10000\n    \n    bool      appendonly;       // no\n    QString   appendfilename;   // appendonly.aof\n    int       appendfsync;      // no, everysec, always\n    \n    int       slowlogtime;      // 1000 microseconds\n    int       slowlogmaxlen;    // 128\n    \n    int       hz;               // 10  [1,500]\n    \n    QString   masterIp;\n    unsigned short masterPort;  // replication\n    QString   masterauth;\n    \n    QString   runid;\n\n    QString   includefile;      // the template config\n\n    std::vector<QString>  modules; // modules\n\n    // use redis as cache, level db as backup\n    uint64_t maxmemory; // default 2GB\n    int maxmemorySamples; // default 5\n    bool noeviction; // default true\n\n    int backend; // enum BackEndType\n    QString backendPath; \n    int backendHz; // the frequency of dump to backend\n\n    // cluster\n    bool enableCluster = false;\n    std::vector<QString> centers;\n    int setid = -1; // sharding set id\n    \n    QConfig();\n\n    bool CheckArgs() const;\n    bool CheckPassword(const QString& pwd) const;\n};\n\nextern  QConfig  g_config;\n\nextern bool  LoadQedisConfig(const char* cfgFile, QConfig& cfg);\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QDB.cc",
    "content": "\n#include \"QDB.h\"\n#include \"Log/Logger.h\"\n#include <sstream>\n#include <unistd.h>\n#include <math.h>\n#include <arpa/inet.h>\n\nextern \"C\"\n{\n#include \"lzf/lzf.h\"\n#include \"redisZipList.h\"\n#include \"redisIntset.h\"\n}\n\nextern \"C\"\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);\n\nnamespace qedis\n{\n\ntime_t  g_lastQDBSave = 0;\npid_t   g_qdbPid = -1;\n\n\n// encoding\nstatic const int8_t kTypeString = 0;\nstatic const int8_t kTypeList   = 1;\nstatic const int8_t kTypeSet    = 2;\nstatic const int8_t kTypeZSet   = 3;\nstatic const int8_t kTypeHash   = 4;\n\nstatic const int8_t kTypeZipMap = 9;\nstatic const int8_t kTypeZipList=10;\nstatic const int8_t kTypeIntSet =11;\nstatic const int8_t kTypeZSetZipList = 12;\nstatic const int8_t kTypeHashZipList = 13;\nstatic const int8_t kTypeQuickList = 14;\n\nstatic const int8_t kQDBVersion = 6;\n\nstatic const int8_t kAux        = 0xFA;\nstatic const int8_t kResizeDb   = 0xFB;\nstatic const int8_t kExpireMs   = 0xFC;\nstatic const int8_t kExpire     = 0xFD;\nstatic const int8_t kSelectDB   = 0xFE;\nstatic const int8_t kEOF        = 0xFF;\n\nstatic const int8_t  k6Bits    = 0;\nstatic const int8_t  k14bits   = 1;\nstatic const int8_t  k32bits   = 2;\nstatic const int8_t  kSpecial  = 3;  //  the string may be interger, or lzf\nstatic const int8_t  kLow6Bits = 0x3F;\n\nstatic const int8_t  kEnc8Bits  = 0;\nstatic const int8_t  kEnc16Bits = 1;\nstatic const int8_t  kEnc32Bits = 2;\nstatic const int8_t  kEncLZF    = 3;\n\nQDBSaver::QDBSaver(const char* qdbFile)\n{\n    if (qdbFile && !qdb_.Open(qdbFile, false))\n        ERR << \"QDBSaver can not open file \" << qdbFile;\n}\n\nvoid  QDBSaver::Save(const char* qdbFile)\n{\n    char tmpFile[64] = \"\";\n    snprintf(tmpFile, sizeof tmpFile, \"tmp_qdb_file_%d\", getpid());\n    \n    if (!qdb_.Open(tmpFile, false))\n        assert (false);\n    \n    char buf[16];\n    snprintf(buf, sizeof buf, \"REDIS%04d\", kQDBVersion);\n    qdb_.Write(buf, 9);\n\n    for (int dbno = 0; true; ++ dbno)\n    {\n        if (QSTORE.SelectDB(dbno) == -1)\n            break;\n\n        if (QSTORE.DBSize() == 0)\n            continue;  // But redis will save empty db\n        \n        qdb_.Write(&kSelectDB, 1);\n        SaveLength(dbno);\n        \n        uint64_t now = ::Now();\n        for (const auto& kv : QSTORE)\n        {\n            int64_t ttl = QSTORE.TTL(kv.first, now);\n            if (ttl > 0)\n            {\n                ttl += now;\n\n                qdb_.Write(&kExpireMs, 1);\n                qdb_.Write(&ttl, sizeof ttl);\n            }\n            else if (ttl == QStore::ExpireResult::expired)\n            {\n                continue;\n            }\n\n            SaveType(kv.second);\n            SaveKey(kv.first);\n            SaveObject(kv.second);\n        }\n    }\n\n    qdb_.Write(&kEOF, 1);\n    \n    // crc 8 bytes\n    InputMemoryFile  file;\n    file.Open(tmpFile);\n    \n    auto len  = qdb_.Offset();\n    auto data = file.Read(len);\n    \n    const uint64_t crc = crc64(0, (const unsigned char* )data, len);\n    qdb_.Write(&crc, sizeof crc);\n    \n    if (::rename(tmpFile, qdbFile) != 0)\n    {\n        perror(\"rename error\");\n        assert (false);\n    }\n}\n\nvoid QDBSaver::SaveType(const QObject& obj)\n{\n    switch (obj.encoding)\n    {\n        case QEncode_raw:\n        case QEncode_int:\n            qdb_.Write(&kTypeString, 1);\n            break;\n                \n        case QEncode_list:\n            qdb_.Write(&kTypeList, 1);\n            break;\n                \n        case QEncode_hash:\n            qdb_.Write(&kTypeHash, 1);\n            break;\n            \n        case QEncode_set:\n            qdb_.Write(&kTypeSet, 1);\n            break;\n            \n        case QEncode_sset:\n            qdb_.Write(&kTypeZSet, 1);\n            break;\n            \n        default:\n            assert(!!!\"Wrong encoding\");\n            break;\n    }\n}\n    \nvoid QDBSaver::SaveKey(const QString& key)\n{\n    SaveString(key);\n}\n\nvoid QDBSaver::SaveObject(const QObject& obj)\n{\n    switch (obj.encoding)\n    {\n        case QEncode_raw:\n        case QEncode_int:\n            SaveString(*GetDecodedString(&obj));\n            break;\n\n        case QEncode_list:\n            _SaveList(obj.CastList());\n            break;\n            \n        case QEncode_set:\n            _SaveSet(obj.CastSet());\n            break;\n            \n        case QEncode_hash:\n            _SaveHash(obj.CastHash());\n            break;\n            \n        case QEncode_sset:\n            _SaveSSet(obj.CastSortedSet());\n            break;\n            \n        default:\n            break;\n    }\n}\n\n/* Copy from redis~\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 */\nvoid  QDBSaver::_SaveDoubleValue(double val)\n{\n    unsigned char buf[128];\n    int len;\n    \n    if (isnan(val)) {\n        buf[0] = 253;\n        len = 1;\n    } else if (!std::isfinite(val)) {\n        len = 1;\n        buf[0] = (val < 0) ? 255 : 254;\n    } else {\n        snprintf((char*)buf+1,sizeof(buf)-1,\"%.6g\",val);\n        buf[0] = strlen((char*)buf+1);\n        len = buf[0]+1;\n    }\n    \n    qdb_.Write(buf,len);\n}\n\n\nvoid QDBSaver::_SaveList(const PLIST& l)\n{\n    SaveLength(l->size());\n    \n    for (const auto& e : *l)\n    {\n        SaveString(e);\n    }\n}\n\n\nvoid  QDBSaver::_SaveSet(const PSET& s)\n{\n    SaveLength(s->size());\n    \n    for (const auto& e : *s)\n    {\n        SaveString(e);\n    }\n}\n\nvoid  QDBSaver::_SaveHash(const PHASH& h)\n{\n    SaveLength(h->size());\n    \n    for (const auto& e : *h)\n    {\n        SaveString(e.first);\n        SaveString(e.second);\n    }\n}\n\n\nvoid    QDBSaver::_SaveSSet(const PSSET& ss)\n{\n    SaveLength(ss->Size());\n    \n    for (const auto& e : *ss)\n    {\n        SaveString(e.first);\n        _SaveDoubleValue(e.second);\n    }\n}\n\nvoid QDBSaver::SaveString(const QString& str)\n{\n    if (str.size() < 10)\n    {\n        long lVal;\n        if (Strtol(str.data(), str.size(), &lVal))\n        {\n            SaveString(lVal);\n            return;\n        }\n    }\n    \n    if (!SaveLZFString(str))\n    {\n        SaveLength(str.size());\n        qdb_.Write(str.data(), str.size());\n    }\n}\n    \n\nvoid QDBSaver::SaveLength(uint64_t len)\n{\n    assert ((len & ~0xFFFFFFFF) == 0);\n  \n    if (len < (1 << 6))\n    {\n        len &= kLow6Bits;\n        len |= k6Bits << 6;\n        qdb_.Write(&len, 1);\n    }\n    else if (len < (1 << 14))\n    {\n        uint16_t encodeLen = (len >> 8) & kLow6Bits;\n        encodeLen |= k14bits << 6;\n        encodeLen |= (len & 0xFF) << 8;\n        qdb_.Write(&encodeLen, 2);\n    }\n    else\n    {\n        int8_t  encFlag = static_cast<int8_t>(k32bits << 6);\n        qdb_.Write(&encFlag, 1);\n        len = htonl(len);\n        qdb_.Write(&len, 4);\n    }\n}\n    \nvoid QDBSaver::SaveString(int64_t intVal)\n{\n    uint8_t specialByte = kSpecial << 6;\n    \n    if ((intVal & ~0x7F) == 0)\n    {\n        specialByte |= kEnc8Bits;\n        qdb_.Write(&specialByte, 1);\n        qdb_.Write(&intVal, 1);\n    }\n    else if ((intVal & ~0x7FFF) == 0)\n    {\n        specialByte |= kEnc16Bits;\n        qdb_.Write(&specialByte, 1);\n        qdb_.Write(&intVal, 2);\n    }\n    else if ((intVal & ~0x7FFFFFFF) == 0)\n    {\n        specialByte |= kEnc32Bits;\n        qdb_.Write(&specialByte, 1);\n        qdb_.Write(&intVal, 4);\n    }\n    else\n    {\n        char buf[64];\n        auto len = Number2Str(buf, sizeof buf, intVal);\n        SaveLength(static_cast<uint64_t>(len));\n        qdb_.Write(buf, len);\n    }\n}\n    \n\nbool QDBSaver::SaveLZFString(const QString& str)\n{\n    if (str.size() < 20)\n        return false;\n        \n    unsigned outlen = static_cast<unsigned>(str.size() - 4);\n    std::unique_ptr<char []> outBuf(new char[outlen + 1]);\n        \n    auto compressLen = lzf_compress((const void*)str.data(), static_cast<unsigned>(str.size()),\n                                        outBuf.get(), outlen);\n    \n    if (compressLen == 0)\n    {\n        ERR << \"compress len = 0\";\n        return false;\n    }\n    \n    int8_t specialByte = static_cast<int8_t>(kSpecial << 6) | kEncLZF;\n    qdb_.Write(&specialByte, 1);\n    \n    // compress len + raw len + str data;\n    SaveLength(compressLen);\n    SaveLength(str.size());\n    qdb_.Write(outBuf.get(), compressLen);\n    \n    DBG << \"compress len \" << compressLen << \", raw len \" << str.size();\n    \n    return  true;\n}\n\n\nvoid QDBSaver::SaveDoneHandler(int exitRet, int whatSignal)\n{\n    if (exitRet == 0 && whatSignal == 0)\n    {\n        INF << \"save rdb success\";\n        g_lastQDBSave = time(NULL);\n        QStore::dirty_ = 0;\n    }\n    else\n    {\n        ERR << \"save rdb failed with exit result \" << exitRet << \", signal \" << whatSignal;\n    }\n    \n    g_qdbPid = -1;\n}\n\n\nQDBLoader::QDBLoader(const char *data, size_t len)\n{\n    if (data && len)\n        qdb_.Attach(data, len);\n}\n\nint  QDBLoader::Load(const char *filename)\n{\n    if (!qdb_.Open(filename))\n    {\n        return - __LINE__;\n    }\n    \n    // check the magic string \"REDIS\" and version number\n    size_t len = 9;\n    const char* data = qdb_.Read(len);\n    \n    if (len != 9)\n    {\n        return - __LINE__;\n    }\n    \n    long qdbversion;\n    if (!Strtol(data + 5, 4, &qdbversion) ||\n        qdbversion < 6)\n    {\n        return - __LINE__;\n    }\n    qdb_.Skip(9);\n    \n    //  SELECTDB + dbno\n    //  type1 + key + obj\n    //  EOF + crc\n\n    int64_t absTimeout = 0;\n    int8_t indicator = 0;\n    bool eof = false;\n    while (!eof)\n    {\n        try {\n            indicator = LoadByte();\n        }\n        catch (const std::runtime_error& e) {\n            ERR << \"LoadByte with exception: \" << e.what();\n            return - __LINE__;\n        }\n        \n        switch (indicator)\n        {\n            case kEOF:\n                DBG << \"encounter EOF\";\n                eof = true;\n                break;\n\n            case kAux:\n                DBG << \"encounter AUX\";\n                try {\n                    _LoadAux();\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"_LoadAux with exception: \" << e.what();\n                    return - __LINE__;\n                }\n                break;\n\n            case kResizeDb:\n                DBG << \"encounter kResizeDb\";\n                try {\n                    _LoadResizeDB();\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"_LoadResizeDB with exception: \" << e.what();\n                    return - __LINE__;\n                }\n                break;\n                \n            case kSelectDB:\n            {\n                bool special;\n                size_t dbno = 0;\n                try {\n                    dbno = LoadLength(special);\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"LoadLength with exception: \" << e.what();\n                    return - __LINE__;\n                }\n\n                if (special)\n                {\n                    ERR << \"LoadLength should not be special\";\n                    return - __LINE__;\n                }\n\n                // check db no\n                if (dbno > kMaxDbNum)\n                {\n                    ERR << \"Abnormal db number \" << dbno;\n                    return __LINE__;\n                }\n\n                if (QSTORE.SelectDB(static_cast<int>(dbno)) == -1)\n                {\n                    ERR << \"DB NUMBER is differ from RDB file\";\n                    return __LINE__;\n                }\n                DBG << \"encounter Select DB \" << dbno;\n                break;\n            }\n                \n            case kExpireMs:\n                try {\n                    absTimeout = qdb_.Read<int64_t>();\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"Read kExpireMs with exception: \" << e.what();\n                    return - __LINE__;\n                }\n\n                break;\n                \n            case kExpire:\n                try {\n                    absTimeout = qdb_.Read<int64_t>();\n                    absTimeout *= 1000;\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"Read kExpire with exception: \" << e.what();\n                    return - __LINE__;\n                }\n\n                break;\n                \n            case kTypeString:\n            case kTypeList:\n            case kTypeZipList:\n            case kTypeSet:\n            case kTypeIntSet:\n            case kTypeHash:\n            case kTypeHashZipList:\n            case kTypeZipMap:\n            case kTypeZSet:\n            case kTypeZSetZipList:\n            case kTypeQuickList:\n            {\n                QString key;\n                QObject obj;\n                try {\n                    key = LoadKey();\n                    obj = LoadObject(indicator);\n                    assert (obj.type != QType_invalid);\n                    DBG << \"encounter key = \" << key << \", obj.encoding = \" << obj.encoding;\n                }\n                catch (const std::runtime_error& e) {\n                    ERR << \"Read object with exception: \" << e.what();\n                    return - __LINE__;\n                }\n\n                if (absTimeout < 0)\n                {\n                    ERR << \"Wrong absTimeout \" << absTimeout;\n                    return - __LINE__;\n                }\n                \n                if (absTimeout == 0)\n                {\n                    QSTORE.SetValue(key, std::move(obj));\n                }\n                else if (absTimeout > 0)\n                {\n                    if (absTimeout > static_cast<int64_t>(::Now()))\n                    {\n                        DBG << key << \" load timeout \" << absTimeout;\n                        QSTORE.SetValue(key, std::move(obj));\n                        QSTORE.SetExpire(key, absTimeout);\n                    }\n                    else\n                    {\n                        INF << key << \" is already time out\";\n                    }\n                    \n                    absTimeout = 0;\n                }\n                break;\n            }\n                \n            default:\n                ERR << indicator << \" is unknown type\";\n                return - __LINE__;\n        }\n    }\n    \n    return 0;\n}\n    \nint8_t QDBLoader::LoadByte()\n{\n    return qdb_.Read<int8_t>();\n}\n\nsize_t  QDBLoader::LoadLength(bool& special)\n{\n    const int8_t byte = qdb_.Read<int8_t>();\n    \n    special = false;\n    size_t  lenResult = 0;\n    \n    switch ((byte & 0xC0) >> 6)\n    {\n        case k6Bits:\n        {\n            lenResult = byte & kLow6Bits;\n            break;\n        }\n            \n        case k14bits:\n        {\n            lenResult = byte & kLow6Bits; // high 6 bits;\n            lenResult <<= 8;\n            \n            const uint8_t bytelow = qdb_.Read<uint8_t>();\n            \n            lenResult |= bytelow;\n            break;\n        }\n            \n        case k32bits:\n        {\n            const int32_t fourbytes = qdb_.Read<int32_t>();\n            \n            lenResult = ntohl(fourbytes);\n            break;\n        }\n            \n        case kSpecial:\n        {\n            special = true;\n            lenResult = byte & kLow6Bits;\n            break;\n        }\n            \n        default:\n        {\n            std::string s(\"Wrong length type:\" + std::to_string(byte));\n            throw std::runtime_error(std::move(s));\n        }\n    }\n\n    return lenResult;\n}\n\n\nQObject QDBLoader::LoadSpecialStringObject(size_t  specialVal)\n{\n    bool isInt = true;\n    long val;\n    \n    switch (specialVal)\n    {\n        case kEnc8Bits:\n        {\n            val = qdb_.Read<uint8_t>();\n            break;\n        }\n            \n        case kEnc16Bits:\n        {\n            val = qdb_.Read<uint16_t>();\n            break;\n        }\n            \n        case kEnc32Bits:\n        {\n            val = qdb_.Read<uint32_t>();\n            break;\n        }\n            \n        case kEncLZF:\n        {\n            isInt = false;\n            break;\n        }\n            \n        default:\n            throw std::runtime_error(\"Wrong specialVal\");\n    }\n    \n    if (isInt)\n        return QObject::CreateString(val);\n    else\n        return QObject::CreateString(LoadLZFString());\n}\n\nQString QDBLoader::LoadString(size_t strLen)\n{\n    const char* str = qdb_.Read(strLen);\n    qdb_.Skip(strLen);\n    \n    return QString(str, strLen);\n}\n\n\nQString QDBLoader::LoadLZFString()\n{\n    bool special;\n    size_t compressLen = LoadLength(special);\n    if (special)\n    {\n        ERR << \"Should not be special for compressLen\";\n        return QString();\n    }\n    \n    unsigned rawLen = static_cast<unsigned>(LoadLength(special));\n    if (special)\n    {\n        ERR << \"Should not be special for rawLen\";\n        return QString();\n    }\n    \n    const char* compressStr = qdb_.Read(compressLen);\n    \n    QString val;\n    val.resize(rawLen);\n    if (lzf_decompress(compressStr, static_cast<unsigned>(compressLen),\n                       &val[0], rawLen) == 0)\n    {\n        ERR << \"decompress error\";\n        return QString();\n    }\n\n    qdb_.Skip(compressLen);\n    return val;\n}\n\nQString QDBLoader::LoadKey()\n{\n    return _LoadGenericString();\n}\n\n\nQObject QDBLoader::LoadObject(int8_t type)\n{\n    switch (type)\n    {\n        case kTypeString:\n        {\n            bool special;\n            size_t len = LoadLength(special);\n\n            if (special)\n            {\n                return LoadSpecialStringObject(len);\n            }\n            else\n            {\n                return QObject::CreateString(LoadString(len));\n            }\n        }\n        case kTypeList:\n        {\n            return _LoadList();\n        }\n        case kTypeZipList:\n        {\n            return _LoadZipList(kTypeZipList);\n        }\n        case kTypeSet:\n        {\n            return _LoadSet();\n        }\n        case kTypeIntSet:\n        {\n            return _LoadIntset();\n        }\n        case kTypeHash:\n        {\n            return _LoadHash();\n        }\n        case kTypeHashZipList:\n        {\n            return _LoadZipList(kTypeHashZipList);\n        }\n        case kTypeZipMap:\n        {\n            assert(!!!\"zipmap should be replaced with ziplist\");\n            break;\n        }\n        case kTypeZSet:\n        {\n            return _LoadSSet();\n        }\n        case kTypeZSetZipList:\n        {\n            return _LoadZipList(kTypeZSetZipList);\n        }\n        case kTypeQuickList:\n        {\n            return _LoadQuickList();\n        }\n            \n        default:\n            break;\n    }\n    \n    return QObject(QType_invalid);\n}\n\n\nQString QDBLoader::_LoadGenericString()\n{\n    bool special;\n    size_t len = LoadLength(special);\n    \n    if (special)\n    {\n        QObject obj = LoadSpecialStringObject(len);\n        return *GetDecodedString(&obj);\n    }\n    else\n    {\n        return LoadString(len);\n    }\n}\n\nQObject QDBLoader::_LoadList()\n{\n    bool special;\n    const auto len = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"LoadList length should not be special\");\n\n    DBG << \"list length = \" << len;\n    \n    QObject obj(QObject::CreateList());\n    PLIST list(obj.CastList());\n    for (size_t i = 0; i < len; ++ i)\n    {\n        const auto elemLen = LoadLength(special);\n        QString  elem;\n        if (special)\n        {\n            QObject str = LoadSpecialStringObject(elemLen);\n            elem = *GetDecodedString(&str);\n        }\n        else\n        {\n            elem = LoadString(elemLen);\n        }\n        \n        list->push_back(elem);\n        DBG << \"list elem : \" << elem.c_str();\n    }\n    \n    return obj;\n}\n\nQObject QDBLoader::_LoadSet()\n{\n    bool special;\n    const auto len = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"LoadSet length should not be special\");\n\n    DBG << \"set length = \" << len;\n    \n    QObject obj(QObject::CreateSet());\n    PSET  set(obj.CastSet());\n    for (size_t i = 0; i < len; ++ i)\n    {\n        const auto elemLen = LoadLength(special);\n        QString  elem;\n        if (special)\n        {\n            QObject str = LoadSpecialStringObject(elemLen);\n            elem = *GetDecodedString(&str);\n        }\n        else\n        {\n            elem = LoadString(elemLen);\n        }\n        \n        set->insert(elem);\n        DBG << \"set elem : \" << elem.c_str();\n    }\n    \n    return obj;\n}\n\nQObject QDBLoader::_LoadHash()\n{\n    bool special;\n    const auto len = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"LoadHash length should not be special\");\n\n    DBG << \"hash length = \" << len;\n    \n    QObject obj(QObject::CreateHash());\n    PHASH  hash(obj.CastHash());\n    for (size_t i = 0; i < len; ++ i)\n    {\n        const auto keyLen = LoadLength(special);\n        QString  key;\n        if (special)\n        {\n            QObject str = LoadSpecialStringObject(keyLen);\n            key = *GetDecodedString(&str);\n        }\n        else\n        {\n            key = LoadString(keyLen);\n        }\n        \n        const auto valLen = LoadLength(special);\n        QString  val;\n        if (special)\n        {\n            QObject str = LoadSpecialStringObject(valLen);\n            val = *GetDecodedString(&str);\n        }\n        else\n        {\n            val = LoadString(valLen);\n        }\n        \n        hash->insert(QHash::value_type(key, val));\n        DBG << \"hash key : \" << key.c_str() << \" val : \" << val.c_str();\n    }\n    \n    return obj;\n}\n\n\nQObject QDBLoader::_LoadSSet()\n{\n    bool special;\n    const auto len = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"LoadSortedSet length should not be special\");\n\n    DBG << \"sset length = \" << len;\n    \n    QObject obj(QObject::CreateSSet());\n    PSSET  sset(obj.CastSortedSet());\n    for (size_t i = 0; i < len; ++ i)\n    {\n        const auto memberLen = LoadLength(special);\n        QString  member;\n        if (special)\n        {\n            QObject str = LoadSpecialStringObject(memberLen);\n            member = *GetDecodedString(&str);\n        }\n        else\n        {\n            member = LoadString(memberLen);\n        }\n        \n        const auto score = _LoadDoubleValue();\n        sset->AddMember(member, static_cast<long>(score));\n        DBG << \"sset member : \" << member.c_str() << \" score : \" << score;\n    }\n    \n    return obj;\n}\n\ndouble  QDBLoader::_LoadDoubleValue()\n{\n    const uint8_t byte1st = qdb_.Read<uint8_t>();\n\n    double  dvalue;\n    switch (byte1st)\n    {\n        case 253:\n        {\n            dvalue = NAN;\n            break;\n        }\n            \n        case 254:\n        {\n            dvalue = INFINITY;\n            break;\n        }\n            \n        case 255:\n        {\n            dvalue = INFINITY;\n            break;\n        }\n            \n        default:\n        {\n            size_t len = byte1st;\n            const char* val = qdb_.Read(len);\n            assert(len == byte1st);\n            qdb_.Skip(len);\n            \n            std::istringstream is(std::string(val, len));\n            is >> dvalue;\n            \n            break;\n        }\n    }\n    \n    DBG << \"load double value \" << dvalue;\n    \n    return  dvalue;\n}\n\nstruct ZipListElement\n{\n    unsigned char* sval;\n    unsigned int   slen;\n    \n    long long      lval;\n    \n    QString ToString() const\n    {\n        if (sval)\n        {\n            const QString str((const char* )sval, QString::size_type(slen));\n            DBG << \"string zip list element \" << str;\n            return str;\n        }\n        else\n            return  LongToString();\n    }\n    \n    QString LongToString() const\n    {\n        assert(!sval);\n\n        QString str(16, 0);\n        auto len = Number2Str(&str[0], 16, lval);\n        str.resize(len);\n        \n        DBG << \"long zip list element \" << str;\n        return str;\n    }\n};\n\nQObject QDBLoader::_LoadZipList(int8_t type)\n{\n    QString zl = _LoadGenericString();\n    return _LoadZipList(zl, type);\n}\n\nQObject QDBLoader::_LoadZipList(const QString& zl, int8_t type)\n{\n    unsigned char* zlist = (unsigned char* )&zl[0];\n    unsigned       nElem = ziplistLen(zlist);\n    \n    std::vector<ZipListElement>  elements;\n    elements.resize(nElem);\n    \n    for (unsigned i = 0; i < nElem; ++ i)\n    {\n        unsigned char* elem = ziplistIndex(zlist, (int)i);\n        \n        int succ = ziplistGet(elem, &elements[i].sval, &elements[i].slen,\n                                    &elements[i].lval);\n        assert (succ);\n    }\n    \n    switch (type)\n    {\n        case kTypeZipList:\n        {\n            QObject  obj(QObject::CreateList());\n            PLIST    list(obj.CastList());\n            \n            for (const auto& elem : elements)\n            {\n                list->push_back(elem.ToString());\n            }\n            \n            return obj;\n        }\n            \n        case kTypeHashZipList:\n        {\n            QObject  obj(QObject::CreateHash());\n            PHASH    hash(obj.CastHash());\n            \n            assert(elements.size() % 2 == 0);\n            \n            for (auto it(elements.begin()); it != elements.end(); ++ it)\n            {\n                auto key = it;\n                auto value = ++ it;\n\n                hash->insert(QHash::value_type(key->ToString(),\n                                               value->ToString()));\n            }\n            \n            return obj;\n        }\n            \n        case kTypeZSetZipList:\n        {\n            QObject  obj(QObject::CreateSSet());\n            PSSET    sset(obj.CastSortedSet());\n\n            assert(elements.size() % 2 == 0);\n            \n            for (auto it(elements.begin()); it != elements.end(); ++ it)\n            {\n                const QString& member = it->ToString();\n                ++ it;\n                \n                double  score;\n                if (it->sval)\n                {\n                    Strtod((const char* )it->sval, it->slen, &score);\n                }\n                else\n                {\n                    score = it->lval;\n                }\n\n                DBG << \"sset member \" << member << \", score \" << score;\n                sset->AddMember(member, score);\n            }\n            \n            return obj;\n        }\n            \n        default:\n            assert(!!!\"illegal data type\");\n            break;\n    }\n    \n    return QObject(QType_invalid);\n}\n\n\nQObject QDBLoader::_LoadIntset()\n{\n    QString str = _LoadGenericString();\n    \n    intset* iset = (intset* )&str[0];\n    unsigned nElem = intsetLen(iset);\n    \n    std::vector<int64_t>  elements;\n    elements.resize(nElem);\n    \n    for (unsigned i = 0; i < nElem; ++ i)\n    {\n        intsetGet(iset, i, &elements[i]);\n    }\n    \n    QObject  obj(QObject::CreateSet());\n    PSET     set(obj.CastSet());\n    \n    for (auto v : elements)\n    {\n        char buf[64];\n        auto bytes = Number2Str<int64_t>(buf, sizeof buf, v);\n        set->insert(QString(buf, bytes));\n    }\n\n    return obj;\n}\n\nQObject QDBLoader::_LoadQuickList()\n{\n    bool special = true;\n    auto nElem = LoadLength(special);\n\n    QObject obj(QObject::CreateList());\n    PLIST list(obj.CastList());\n    while (nElem -- > 0)\n    {\n        QString zl = _LoadGenericString();\n        if (zl.empty())\n            continue;\n\n        QObject l = _LoadZipList(zl, kTypeZipList);\n        PLIST tmplist(l.CastList());\n        if (!tmplist->empty())\n            list->splice(list->end(), *tmplist);\n    }\n\n    return obj;\n}\n\nvoid QDBLoader::_LoadAux()\n{\n    /* AUX: generic string-string fields. Use to add state to RDB\n     * which is backward compatible. Implementations of RDB loading\n     * are required to skip AUX fields they don't understand.\n     * \n     * An AUX field is composed of two strings: key and value. */\n    QString auxkey = _LoadGenericString();\n    QString auxvalue = _LoadGenericString();\n\n    if (!auxkey.empty() && auxkey[0] == '%')\n    {\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        USR << \"RDB '\" << auxkey << \"': \" << auxvalue;\n    }\n    else\n    {\n        /* We ignore fields we don't understand, as by AUX field \n         * contract. */\n        DBG << \"Unrecognized RDB AUX field: '\" << auxkey << \"': \" << auxvalue;\n    }\n}\n\nvoid QDBLoader::_LoadResizeDB()\n{\n    bool special = true;\n    auto dbsize = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"Should not be special when LoadLength\");\n\n    auto expiresize = LoadLength(special);\n    if (special)\n        throw std::runtime_error(\"Should not be special when LoadLength\");\n\n    // Qedis just ignore this\n    (void)special;\n    (void)dbsize;\n    (void)expiresize;\n}\n\n\nstd::string DumpObject(const QObject& val)\n{\n    std::string file = \"qedisdump\";\n    file += std::to_string(getpid());\n    {\n        QDBSaver saver(file.data());\n        saver.SaveType(val);\n        saver.SaveObject(val);\n    }\n\n    // 2 bytes version\n    char v[2];\n    v[0] = kQDBVersion & 0xFF;\n    v[1] = (kQDBVersion >> 8) & 0xFF;\n\n    InputMemoryFile ifile;\n    ifile.Open(file.data());\n\n    std::size_t size = std::numeric_limits<std::size_t>::max();\n    const char* data = ifile.Read(size);\n\n    std::string result(data, size);\n    result.append(v, 2);\n    // 8 bytes crc\n    const uint64_t crc = crc64(0, (const unsigned char* )result.data(), result.size());\n    result.append((const char*)&crc, 8);\n\n    unlink(file.data());\n    return result;\n}\n\nQObject RestoreObject(const char* data, size_t len)\n{\n    try {\n        QDBLoader loader(data, len);\n        int8_t type = loader.LoadByte();\n        QObject obj = loader.LoadObject(type);\n\n        // check version\n        char v[2];\n        v[0] = loader.LoadByte();\n        v[1] = loader.LoadByte();\n        if (v[0] != kQDBVersion)\n            return QObject();\n\n        // check crc\n        uint64_t crc = 0;\n        unsigned char* p = (unsigned char*)&crc;\n        for (size_t i = 0; i < sizeof(crc); ++ i)\n        {\n            p[i] = loader.LoadByte();\n        }\n\n        const uint64_t expectCrc = crc64(0, (const unsigned char* )data, len - 8);\n        if (expectCrc != crc)\n            return QObject();\n\n        return obj;\n    }\n    catch (const std::runtime_error& e) {\n        ERR << \"RestoreObject with exception: \" << e.what();\n        return QObject();\n    }\n}\n\nQError dump(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* val;\n    if (QSTORE.GetValue(params[1], val) != QError_ok)\n    {\n        ReplyError(QError_notExist, reply);\n        return QError_notExist;\n    }\n\n    std::string str(DumpObject(*val));\n    FormatBulk(str, reply);\n    return QError_ok;\n}\n\n// restore key ttl ser-val replace\nQError restore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject obj(RestoreObject(params[3].data(), params[3].size()));\n    if (obj.type == QType_invalid)\n    {\n        char err[] = \"-ERR DUMP payload version or checksum are wrong\\r\\n\";\n        if (reply) reply->PushData(err, sizeof err - 1);\n        return QError_nop;\n    }\n\n    long ttl = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &ttl))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    bool replace = false;\n    if (params.size() == 5 && params[4] == \"replace\")\n        replace = true;\n\n    const auto& key = params[1];\n    if (!replace && QSTORE.GetObject(key))\n    {\n        USR << \"Can not replace \" << key;\n        ReplyError(QError_busykey, reply);\n        return QError_busykey;\n    }\n\n    QSTORE.SetValue(key, std::move(obj));\n    if (ttl > 0)\n        QSTORE.SetExpireAfter(key, ttl);\n\n    FormatOK(reply);\n    return QError_ok;\n}\n\n} // end namespace qedis\n\n"
  },
  {
    "path": "QedisCore/QDB.h",
    "content": "#ifndef BERT_QDB_H\n#define BERT_QDB_H\n\n#include \"Log/MemoryFile.h\"\n#include \"QStore.h\"\n\nnamespace qedis\n{\n\nclass QDBSaver\n{\npublic:\n    explicit\n    QDBSaver(const char* file = nullptr);\n    void    Save(const char* qdbFile);\n    void    SaveType(const QObject& obj);\n    void    SaveKey(const QString& key);\n    void    SaveObject(const QObject& obj);\n    void    SaveString(const QString& str);\n    void    SaveLength(uint64_t len);   // big endian\n    void    SaveString(int64_t intVal);\n    bool    SaveLZFString(const QString& str);\n    \n    static  void SaveDoneHandler(int exit, int signal);\n\nprivate:\n    void    _SaveDoubleValue(double val);\n    \n    void    _SaveList(const PLIST& l);\n    void    _SaveSet(const PSET& s);\n    void    _SaveHash(const PHASH& h);\n    void    _SaveSSet(const PSSET& ss);\n   \n    OutputMemoryFile  qdb_;\n};\n\nextern time_t g_lastQDBSave;\nextern pid_t  g_qdbPid;\n\nclass QDBLoader\n{\npublic:\n    explicit\n    QDBLoader(const char* data = nullptr, size_t len = 0);\n    int Load(const char* filename);\n\n    int8_t  LoadByte();\n    size_t  LoadLength(bool& special);\n    QObject LoadSpecialStringObject(size_t  specialVal);\n    QString LoadString(size_t strLen);\n    QString LoadLZFString();\n\n    QString LoadKey();\n    QObject LoadObject(int8_t type);\n\nprivate:\n    QString _LoadGenericString();\n    QObject _LoadList();\n    QObject _LoadSet();\n    QObject _LoadHash();\n    QObject _LoadSSet();\n    double  _LoadDoubleValue();\n    QObject _LoadZipList(int8_t type);\n    QObject _LoadZipList(const QString& zl, int8_t type);\n    QObject _LoadIntset();\n    QObject _LoadQuickList();\n\n    void    _LoadAux();\n    void    _LoadResizeDB();\n    \n    InputMemoryFile qdb_;\n};\n    \nstd::string DumpObject(const QObject& val);\nQObject RestoreObject(const char* data, size_t len);\n\n}\n\n#endif\n"
  },
  {
    "path": "QedisCore/QDumpInterface.h",
    "content": "#ifndef BERT_QDUMPINTERFACE_H\n#define BERT_QDUMPINTERFACE_H\n\n#include <stdint.h>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nstruct QObject;\n\nclass QDumpInterface\n{\npublic:\n    virtual ~QDumpInterface() {}\n\n    virtual QObject Get(const QString& key) = 0;\n    virtual bool Put(const QString& key, const QObject& obj, int64_t ttl = 0) = 0;\n    virtual bool Put(const QString& key) = 0;\n    virtual bool Delete(const QString& key) = 0;\n\n    //std::vector<QObject> MultiGet(const QString& key);\n    //bool MultiPut(const QString& key, const QObject& obj, int64_t ttl = 0);\n    //SaveAllRedisTolevelDb();\n    //LoadAllLeveldbToRedis();\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QGlobRegex.cc",
    "content": "#include <cassert>\n#include <set>\n#include \"QGlobRegex.h\"\n\nnamespace qedis\n{\n\nbool NotGlobRegex(const char* pattern, std::size_t plen)\n{\n    for (std::size_t i(0); i < plen; ++ i)\n    {\n        if (pattern[i] == '?' ||\n            pattern[i] == '\\\\' ||\n            pattern[i] == '[' ||\n            pattern[i] == ']' ||\n            pattern[i] == '*' ||\n            pattern[i] == '^' ||\n            pattern[i] == '-')\n            return false; //  may be regex, may not, who cares?\n    }\n\n    return true; // must not be regex\n}\n\nQGlobRegex::QGlobRegex(const char* pattern, std::size_t plen,\n                       const char* text, std::size_t tlen)\n{\n    SetPattern(pattern, plen);\n    SetText(text, tlen);\n}\n\nvoid QGlobRegex::SetPattern(const char* pattern, std::size_t plen)\n{\n    pattern_ = pattern;\n    pLen_ = plen;\n    pOff_ = 0;\n}\n\nvoid QGlobRegex::SetText(const char* text, std::size_t tlen)\n{\n    text_ = text;\n    tLen_ = tlen;\n    tOff_ = 0;\n}\n\nbool QGlobRegex::TryMatch()\n{\n    while (pOff_ < pLen_)\n    {\n        switch (pattern_[pOff_])\n        {\n        case '*':\n            return  _ProcessStar();\n\n        case '?':\n            if (!_ProcessQuestion())\n                return false;\n\n            break;\n\n        case '[':\n            if (!_ProcessBracket())\n                return false;\n\n            break;\n\n        case '\\\\':\n            if (pOff_ + 1 < pLen_ &&\n                pattern_[pOff_ + 1] == '[')\n                ++ pOff_;\n            // fall through;\n\n        default:\n            if (tOff_ >= tLen_)\n                return false;\n\n            if (pattern_[pOff_] != text_[tOff_])\n                return false;\n\n            ++ pOff_;\n            ++ tOff_;\n            break;\n        }\n    }\n    \n    return _IsMatch();\n}\n\nbool QGlobRegex::_ProcessStar()\n{\n    assert(pattern_[pOff_] == '*');\n\n    do\n    {\n        ++ pOff_;\n    } while (pOff_ < pLen_ && pattern_[pOff_] == '*');\n\n    if (pOff_ == pLen_)\n        return  true;\n\n    while (tOff_ < tLen_)\n    {\n        std::size_t oldpoff = pOff_;\n        std::size_t oldtoff = tOff_;\n        if (TryMatch())\n        {\n            return true;\n        }\n\n        pOff_ = oldpoff;\n        tOff_ = oldtoff;\n        ++ tOff_;\n    }\n\n    return false;\n}\n\nbool QGlobRegex::_ProcessQuestion()\n{\n    assert(pattern_[pOff_] == '?');\n\n    while (pOff_ < pLen_)\n    {\n        if (pattern_[pOff_] != '?')\n            break;\n\n        if (tOff_ == tLen_)\n        {\n            return false; // str is too short\n        }\n\n        ++ pOff_;\n        ++ tOff_;\n    }\n\n    return true;\n}\n\n            \nbool  QGlobRegex::_ProcessBracket()\n{\n    assert(pattern_[pOff_] == '[');\n\n    if (pOff_ + 1 >= pLen_)\n    {\n        //std::cerr << \"expect ] at end\\n\";\n        return  false;\n    }\n\n    ++ pOff_;\n\n    bool  include = true;\n    if (pattern_[pOff_] == '^')\n    {\n        include = false;\n        ++ pOff_;\n    }\n\n    std::set<char>  chars;\n    \n    if (pOff_ < pLen_ && pattern_[pOff_] == ']')\n    {\n        chars.insert(']'); // No allowed empty brackets.\n        ++ pOff_;\n    }\n\n    std::set<std::pair<int, int> >  spans;\n    while (pOff_ < pLen_ && pattern_[pOff_] != ']')\n    {\n        if ((pOff_ + 3) < pLen_ && pattern_[pOff_ + 1] == '-')\n        {\n            int start = pattern_[pOff_];\n            int end   = pattern_[pOff_ + 2];\n\n            if (start == end)\n            {\n                chars.insert(start);\n            }\n            else\n            {\n                if (start > end)\n                    std::swap(start, end);\n\n                spans.insert(std::make_pair(start, end));\n            }\n\n            pOff_ += 3;\n        }\n        else \n        {\n            chars.insert(pattern_[pOff_]);\n            ++ pOff_;\n        }\n    }\n\n    if (pOff_ == pLen_)\n    {\n        //std::cerr << \"expect ]\\n\";\n        return  false;\n    }\n    else\n    {\n        assert (pattern_[pOff_] == ']');\n        ++ pOff_;\n    }\n\n    if (chars.count(text_[tOff_]) > 0)\n    {\n        if (include)\n        {\n            ++ tOff_;\n            return true;\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    for (const auto& pair : spans)\n    {\n        if (text_[tOff_] >= pair.first && text_[tOff_] <= pair.second)\n        {\n            if (include)\n            {\n                ++ tOff_;\n                return true;\n            }\n            else\n            {\n                return false;\n            }\n        }\n    }\n\n    if (include)\n    {\n        return false;\n    }\n    else\n    {\n        ++ tOff_;\n        return true;\n    }\n}\n\nbool QGlobRegex::_IsMatch() const\n{\n    return pOff_ == pLen_ && tLen_ == tOff_;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QGlobRegex.h",
    "content": "#ifndef BERT_QGLOBREGEX_H\n#define BERT_QGLOBREGEX_H\n\n#include <string.h>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nbool NotGlobRegex(const char* pattern, std::size_t plen);\n\nclass QGlobRegex\n{\npublic:\n    explicit QGlobRegex(const char* pattern = 0, std::size_t plen = 0,\n                        const char* text = 0, std::size_t tlen = 0);\n\n    void SetPattern(const char* pattern, std::size_t plen);\n    void SetText(const char* text, std::size_t tlen);\n    bool TryMatch();\n\n\nprivate:\n    bool _ProcessStar();\n    bool _ProcessQuestion();\n    bool _ProcessBracket();\n    bool _IsMatch() const;\n\n    const char*     pattern_;\n    std::size_t     pLen_;\n    std::size_t     pOff_;\n\n    const char*     text_;\n    std::size_t     tLen_;\n    std::size_t     tOff_;\n};\n\ninline bool glob_match(const char* pattern, std::size_t plen,\n                       const char* text, std::size_t tlen)\n{\n    QGlobRegex   rgx;\n    rgx.SetPattern(pattern, plen);\n    rgx.SetText(text, tlen);\n\n    return rgx.TryMatch();\n}\n\ninline bool glob_match(const char* pattern,\n                       const char* text)\n{\n    return glob_match(pattern, strlen(pattern), text, strlen(text));\n}\n\ninline bool glob_match(const QString& pattern,\n                       const QString& text)\n{\n    return glob_match(pattern.c_str(), pattern.size(),\n                      text.c_str(), text.size());\n}\n\ninline bool glob_search(const char* pattern,\n                       const char* str)\n{\n    QString sPattern(\"*\");\n    sPattern += pattern;\n    sPattern += \"*\";\n\n    QGlobRegex   rgx;\n    rgx.SetPattern(sPattern.c_str(), sPattern.size());\n    rgx.SetText(str, strlen(str));\n\n    return rgx.TryMatch();\n}\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QHash.cc",
    "content": "#include \"QHash.h\"\n#include \"QStore.h\"\n#include <cassert>\n\nnamespace qedis\n{\n\nQObject QObject::CreateHash()\n{\n    QObject obj(QType_hash);\n    obj.Reset(new QHash);\n    return obj;\n}\n\n#define GET_HASH(hashname)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(hashname, value, QType_hash);  \\\n    if (err != QError_ok)  {  \\\n        ReplyError(err, reply); \\\n        return err;  \\\n}\n\n#define GET_OR_SET_HASH(hashname)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(hashname, value, QType_hash);  \\\n    if (err != QError_ok && err != QError_notExist)  {  \\\n        ReplyError(err, reply); \\\n        return err;  \\\n    }   \\\n    if (err == QError_notExist) { \\\n        value = QSTORE.SetValue(hashname, QObject::CreateHash());  \\\n    }\n\n\nQHash::iterator _set_hash_force(QHash& hash, const QString& key, const QString& val)\n{\n    auto it(hash.find(key));\n    if (it != hash.end())\n        it->second = val;\n    else\n        it = hash.insert(QHash::value_type(key, val)).first;\n\n    return it;\n}\n\nbool _set_hash_if_notexist(QHash& hash, const QString& key, const QString& val)\n{\n    return hash.insert(QHash::value_type(key, val)).second;\n}\n\nQError hset(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_HASH(params[1]);\n    \n    auto hash= value->CastHash();\n    _set_hash_force(*hash, params[2], params[3]);\n    \n    FormatInt(1, reply);\n    return QError_ok;\n}\n\nQError hmset(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 != 0)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    GET_OR_SET_HASH(params[1]);\n\n    auto hash= value->CastHash();\n    for (size_t i = 2; i < params.size(); i += 2)\n        _set_hash_force(*hash, params[i], params[i + 1]);\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError hget(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n    \n    auto hash= value->CastHash();\n    auto it = hash->find(params[2]);\n\n    if (it != hash->end())\n        FormatBulk(it->second, reply);\n    else\n        FormatNull(reply);\n\n    return QError_ok;\n}\n\n\nQError hmget(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    PreFormatMultiBulk(params.size() - 2, reply);\n\n    auto hash= value->CastHash();\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        auto it = hash->find(params[i]);\n        if (it != hash->end())\n            FormatBulk(it->second, reply);\n        else\n            FormatNull(reply);\n    }\n    \n    return QError_ok;\n}\n\nQError hgetall(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    auto hash = value->CastHash();\n    PreFormatMultiBulk(2 * hash->size(), reply);\n    \n    for (const auto& kv : *hash)\n    {\n        FormatBulk(kv.first, reply);\n        FormatBulk(kv.second, reply);\n    }\n    \n    return QError_ok;\n}\n\nQError hkeys(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    auto hash = value->CastHash();\n    PreFormatMultiBulk(hash->size(), reply);\n\n    for (const auto& kv : *hash)\n    {\n        FormatBulk(kv.first, reply);\n    }\n    \n    return QError_ok;\n}\n\nQError hvals(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    auto hash = value->CastHash();\n    PreFormatMultiBulk(hash->size(), reply);\n    \n    for (const auto& kv : *hash)\n    {\n        FormatBulk(kv.second, reply);\n    }\n    \n    return QError_ok;\n}\n\nQError  hdel(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_hash);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n\n    int del = 0;\n    auto hash = value->CastHash();\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        auto it = hash->find(params[i]);\n\n        if (it != hash->end())\n        {\n            hash->erase(it);\n            ++ del;\n        }\n    }\n            \n    FormatInt(del, reply);\n    return QError_ok;\n}\n\nQError hexists(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    auto hash= value->CastHash();\n    auto it = hash->find(params[2]);\n\n    if (it != hash->end())\n        FormatInt(1, reply);\n    else\n        FormatInt(0, reply);\n\n    return QError_ok;\n}\n\nQError hlen(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_HASH(params[1]);\n\n    auto hash= value->CastHash();\n    FormatInt(hash->size(), reply);\n    return QError_ok;\n}\n\nQError hincrby(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_HASH(params[1]);\n    \n    auto hash = value->CastHash();\n    long val = 0;\n    QString* str = nullptr;\n    auto it(hash->find(params[2]));\n    if (it != hash->end())\n    {\n        str = &it->second;\n        if (Strtol(str->c_str(), static_cast<int>(str->size()), &val))\n        {\n            val += atoi(params[3].c_str());\n        }\n        else\n        {\n            ReplyError(QError_nan, reply);\n            return QError_nan;\n        }\n    }\n    else\n    {\n        val = atoi(params[3].c_str());\n        auto it = _set_hash_force(*hash, params[2], \"\");\n        str = &it->second;\n    }\n\n    char tmp[32];\n    snprintf(tmp, sizeof tmp - 1, \"%ld\", val);\n    *str = tmp;\n\n    FormatInt(val, reply);\n    return QError_ok;\n}\n\nQError hincrbyfloat(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_HASH(params[1]);\n    \n    auto hash = value->CastHash();\n    float val = 0;\n    QString* str = 0;\n    auto it(hash->find(params[2]));\n    if (it != hash->end())\n    {\n        str = &it->second;\n        if (Strtof(str->c_str(), static_cast<int>(str->size()), &val))\n        {\n            val += atof(params[3].c_str());\n        }\n        else\n        {\n            ReplyError(QError_param, reply);\n            return QError_param;\n        }\n    }\n    else\n    {\n        val = atof(params[3].c_str());\n        auto it = _set_hash_force(*hash, params[2], \"\");\n        str = &it->second;\n    }\n\n    char tmp[32];\n    snprintf(tmp, sizeof tmp - 1, \"%f\", val);\n    *str = tmp;\n\n    FormatBulk(*str, reply);\n    return QError_ok;\n}\n\nQError hsetnx(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_HASH(params[1]);\n    \n    auto hash= value->CastHash();\n    if (_set_hash_if_notexist(*hash, params[2], params[3]))\n        FormatInt(1, reply);\n    else\n        FormatInt(0, reply);\n\n    return QError_ok;\n}\n\nQError hstrlen(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_hash);\n    if (err != QError_ok) \n    {\n        Format0(reply);\n        return err;\n    }\n    \n    auto hash= value->CastHash();\n    auto it = hash->find(params[2]);\n    if (it == hash->end())\n        Format0(reply);\n    else\n        FormatInt(static_cast<long>(it->second.size()), reply);\n\n    return QError_ok;\n}\n\nsize_t HScanKey(const QHash& hash, size_t cursor, size_t count, std::vector<QString>& res)\n{\n    if (hash.empty())\n        return 0;\n    \n    std::vector<QHash::const_local_iterator> iters;\n    size_t newCursor = ScanHashMember(hash, cursor, count, iters);\n    \n    res.reserve(iters.size());\n    for (auto it : iters)\n        res.push_back(it->first), res.push_back(it->second);\n    \n    return newCursor;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QHash.h",
    "content": "#ifndef BERT_QHASH_H\n#define BERT_QHASH_H\n\n#include \"QString.h\"\n#include \"QHelper.h\"\n\n#include <unordered_map>\n\nnamespace qedis\n{\n\nusing QHash = std::unordered_map<QString, QString, my_hash, std::equal_to<QString> >;\n\n\nsize_t   HScanKey(const QHash& hash, size_t cursor, size_t count, std::vector<QString>& res);\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QHelper.cc",
    "content": "#include <unistd.h>\n#include <sys/time.h>\n#include <string.h>\n#include <fstream>\n\n#include \"QHelper.h\"\n\n#if defined(__gnu_linux__)\n#include <fcntl.h>\n\n#elif defined(__APPLE__)\n#include <mach/task.h>\n#include <mach/mach_init.h>\n\n#else\n#error \"unknow platform\"\n\n#endif\n\nnamespace qedis\n{\n\nstatic unsigned dict_hash_function_seed = 5381;\n\n// hash func from redis\nunsigned int dictGenHashFunction(const void* key, int len) {\n    /* 'm' and 'r' are mixing constants generated offline.\n     They're not really 'magic', they just happen to work well.  */\n    unsigned seed = dict_hash_function_seed;\n    const unsigned m = 0x5bd1e995;\n    const int r = 24;\n\n    /* Initialize the hash to a 'random' value */\n    unsigned h = seed ^ len;\n\n    /* Mix 4 bytes at a time into the hash */\n    const unsigned char *data = (const unsigned char *)key;\n\n    while(len >= 4) {\n        unsigned int k = *(unsigned int*)data;\n\n        k *= m;\n        k ^= k >> r;\n        k *= m;\n\n        h *= m;\n        h ^= k;\n\n        data += 4;\n        len -= 4;\n    }\n\n    /* Handle the last few bytes of the input array  */\n    switch(len) {\n        case 3: h ^= data[2] << 16;\n        case 2: h ^= data[1] << 8;\n        case 1: h ^= data[0]; h *= m;\n    };\n\n    /* Do a few final mixes of the hash to ensure the last few\n     * bytes are well-incorporated. */\n    h ^= h >> 13;\n    h *= m;\n    h ^= h >> 15;\n\n    return (unsigned int)h;\n}\n\n// hash function\nsize_t my_hash::operator() (const QString& str) const {\n    return dictGenHashFunction(str.data(), static_cast<int>(str.size()));\n}\n\n\nstatic const uint8_t 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\nstd::size_t BitCount(const uint8_t*  buf, std::size_t len)\n{\n    std::size_t cnt = 0;\n    std::size_t loop = len / 4;\n    std::size_t remain = len % 4;\n\n    for (std::size_t i = 0; i  < loop; ++ i)\n    {\n        cnt += bitsinbyte[buf[4 * i]];\n        cnt += bitsinbyte[buf[4 * i + 1]];\n        cnt += bitsinbyte[buf[4 * i + 2]];\n        cnt += bitsinbyte[buf[4 * i + 3]];\n    }\n\n    for (std::size_t i = 0; i  < remain; ++ i)\n    {\n        cnt += bitsinbyte[buf[4 * loop + i]];\n    }\n\n    return cnt;\n}\n    \n/* Copy from redis source.\n * Generate the Redis \"Run ID\", a SHA1-sized random number that identifies a\n * given execution of Redis, so that if you are talking with an instance\n * having run_id == A, and you reconnect and it has run_id == B, you can be\n * sure that it is either a different instance or it was restarted. */\nvoid getRandomHexChars(char *p, unsigned int len)\n{\n    FILE *fp = fopen(\"/dev/urandom\",\"r\");\n    const char *charset = \"0123456789abcdef\";\n    unsigned int j;\n        \n    if (fp == NULL || fread(p,len,1,fp) == 0) {\n        /* If we can't read from /dev/urandom, do some reasonable effort\n         * in order to create some entropy, since this function is used to\n         * generate run_id and cluster instance IDs */\n        char *x = p;\n        unsigned int l = len;\n        struct timeval tv;\n        pid_t pid = getpid();\n            \n        /* Use time and PID to fill the initial array. */\n        gettimeofday(&tv,NULL);\n        if (l >= sizeof(tv.tv_usec)) {\n            memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec));\n            l -= sizeof(tv.tv_usec);\n            x += sizeof(tv.tv_usec);\n        }\n        if (l >= sizeof(tv.tv_sec)) {\n            memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec));\n            l -= sizeof(tv.tv_sec);\n            x += sizeof(tv.tv_sec);\n        }\n        if (l >= sizeof(pid)) {\n            memcpy(x,&pid,sizeof(pid));\n            l -= sizeof(pid);\n            x += sizeof(pid);\n        }\n        /* Finally xor it with rand() output, that was already seeded with\n         * time() at startup. */\n        for (j = 0; j < len; j++)\n            p[j] ^= rand();\n    }\n    /* Turn it into hex digits taking just 4 bits out of 8 for every byte. */\n    for (j = 0; j < len; j++)\n        p[j] = charset[p[j] & 0x0F];\n    fclose(fp);\n}\n\n#if defined(__gnu_linux__)\n/*\n/proc/<PID>/status Field Description Option Explanation\n\nVmPeak   \npeak virtual memory size\n\nVmSize\nThis is the process's virtual set size, which is the amount of virtual memory that the application is using.\nIt's the same as the vsz parameter provided by ps.\n\nVmLck\nThis is the amount of memory that has been locked by this process. Locked memory cannot be swapped to disk.\n\nVmHWM : peak resident set size (\"high water mark\")\nVmRSS\nThis is the resident set size or amount of physical memory the application is currently using. This is the same as the rss statistic provided by ps.\n\nVmData\nThis is the data size or the virtual size of the program's data usage. Unlike ps dsiz statistic, this does not include stack information.\n\nVmStk\nThis is the size of the process's stack.\n\nVmExe\nThis is the virtual size of the executable memory that the program has. It does not include libraries that the process is using.\n\nVmLib\nSize of shared library code. This is the size of the libraries that the process is using.\n\nVmSwap \namount of swap used by anonymous private data(shmem swap usage is not included)\n*/\n         \nstd::vector<size_t> getMemoryInfo()\n{\n    /*\n    VmPeak = 0,\n    VmSize = 1,\n    VmLck = 2,\n    VmHWM = 3,\n    VmRSS = 4,\n    VmSwap = 5, */\n    std::vector<size_t>  res(VmMax);\n    //int page = sysconf(_SC_PAGESIZE);\n\n    char filename[64];\n    snprintf(filename, sizeof filename, \"/proc/%d/status\", getpid());\n    std::ifstream ifs(filename);\n    std::string line;\n    int count = 0;\n    while (count < VmMax && std::getline(ifs, line))\n    {\n        auto it(res.begin());\n        if (line.find(\"VmPeak\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmPeak);\n        }\n        else if (line.find(\"VmSize\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmSize);\n        }\n        else if (line.find(\"VmLck\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmLck);\n        }\n        else if (line.find(\"VmHWM\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmHWM);\n        }\n        else if (line.find(\"VmRSS\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmRSS);\n        }\n        else if (line.find(\"VmSwap\") == 0)\n        {\n            ++ count;\n            std::advance(it, VmSwap);\n        }\n        else\n        {\n            continue;\n        }\n\n        // skip until number;\n        std::size_t offset = 0;\n        while (offset < line.size() && !isdigit(line[offset]))\n            ++ offset;\n\n        if (offset < line.size())\n        {\n            char* end = nullptr;\n            long val = strtol(&line[offset], &end, 0);\n            val *= 1024; // since suffix is KB\n            *it = static_cast<size_t>(val);\n        }\n    }\n\n    return res;\n}\n\nsize_t getMemoryInfo(MemoryInfoType type)\n{\n    char filename[64];\n    snprintf(filename, sizeof filename, \"/proc/%d/status\", getpid());\n    std::ifstream ifs(filename);\n    std::string line;\n    bool found = false;\n    while (!found && std::getline(ifs, line))\n    {\n        switch (type)\n        {\n            case VmPeak:\n                if (line.find(\"VmPeak\") == 0)\n                    found = true;\n                break;\n\n            case VmSize:\n                if (line.find(\"VmSize\") == 0)\n                    found = true;\n                break;\n\n            case VmLck:\n                if (line.find(\"VmLck\") == 0)\n                    found = true;\n                break;\n\n            case VmHWM:\n                if (line.find(\"VmHWM\") == 0)\n                    found = true;\n                break;\n\n            case VmRSS:\n                if (line.find(\"VmRSS\") == 0)\n                    found = true;\n                break;\n\n            case VmSwap:\n                if (line.find(\"VmSwap\") == 0)\n                    found = true;\n                break;\n\n            default:\n                return 0;\n        }\n    }\n\n    // skip until number;\n    std::size_t offset = 0;\n    while (offset < line.size() && !isdigit(line[offset]))\n        ++ offset;\n\n    size_t  res = 0;\n    if (offset < line.size())\n    {\n        char* end = nullptr;\n        long val = strtol(&line[offset], &end, 0);\n        val *= 1024; // since suffix is KB\n        res = static_cast<size_t>(val);\n    }\n\n    return res;\n}\n\n#elif defined(__APPLE__)\nstd::vector<size_t> getMemoryInfo()\n{\n    /* only support\n     mach_vm_size_t  virtual_size;   virtual memory size (bytes)\n     mach_vm_size_t  resident_size;      resident memory size (bytes)\n     mach_vm_size_t  resident_size_max;  maximum resident memory size (bytes) */\n    std::vector<size_t> res(VmMax);\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 res;\n\n    task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);\n\n    res[VmSize] = t_info.virtual_size;\n    res[VmRSS] = t_info.resident_size;\n\n    return res;\n}\n\nsize_t getMemoryInfo(MemoryInfoType type)\n{\n    if (type != VmSize && type != VmRSS)\n        return 0;\n\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\n    task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);\n\n    if (type == VmSize)\n        return t_info.virtual_size;\n    else if (type == VmRSS)\n        return t_info.resident_size;\n\n    return 0;\n}\n\n#endif\n\n}\n\n"
  },
  {
    "path": "QedisCore/QHelper.h",
    "content": "#ifndef BERT_HELPER_H\n#define BERT_HELPER_H\n\n#include \"QString.h\"\n#include <vector> \n#include <cstdlib> \n\nnamespace qedis\n{\n\n// hash func from redis\nextern unsigned int dictGenHashFunction(const void* key, int len);\n\n// hash function\nstruct my_hash\n{\n    size_t operator() (const QString& str) const;\n};\n\nstd::size_t BitCount(const uint8_t*  buf, std::size_t len);\n\ntemplate <typename HASH>\ninline typename HASH::const_local_iterator RandomHashMember(const HASH& container)\n{\n    if (container.empty())\n    {\n        return typename HASH::const_local_iterator();\n    }\n    \n    while (true)\n    {\n        size_t bucket = random() % container.bucket_count();\n        if (container.bucket_size(bucket) == 0)\n            continue;\n        \n        long lucky = random() % container.bucket_size(bucket);\n        typename HASH::const_local_iterator it = container.begin(bucket);\n        while (lucky > 0)\n        {\n            ++ it;\n            -- lucky;\n        }\n        \n        return it;\n    }\n    \n    \n    return typename HASH::const_local_iterator();\n}\n\n// scan\ntemplate <typename HASH>\ninline size_t ScanHashMember(const HASH& container,\n                             size_t cursor,\n                             size_t count,\n                             std::vector<typename HASH::const_local_iterator>& res)\n{\n    if (cursor >= container.size())\n    {\n        return 0;\n    }\n\n    auto idx = cursor;\n    for (decltype(container.bucket_count()) bucket = 0;\n         bucket < container.bucket_count();\n         ++ bucket)\n    {\n        const auto bktSize = container.bucket_size(bucket);\n        if (idx < bktSize)\n        {\n            // find the bucket;\n            auto   it = container.begin(bucket);\n            while (idx > 0)\n            {\n                ++ it;\n                -- idx;\n            }\n\n            size_t newCursor = cursor;\n            auto   end = container.end(bucket);\n            while (res.size() < count && it != end)\n            {\n                ++ newCursor;\n                res.push_back(it ++);\n\n                if (it == end)\n                {\n                    while (++ bucket < container.bucket_count())\n                    {\n                        if (container.bucket_size(bucket) > 0)\n                        {\n                            it  = container.begin(bucket);\n                            end = container.end(bucket);\n                            break;\n                        }\n                    }\n\n                    if (bucket == container.bucket_count())\n                        return  0;\n                }\n            }\n\n            return  newCursor;\n        }\n        else\n        {\n            idx -= bktSize;\n        }\n    }\n\n    return 0;   // never here\n}\n    \nextern void getRandomHexChars(char *p, unsigned int len);\n\nenum MemoryInfoType {\n    VmPeak = 0,\n    VmSize = 1,\n    VmLck = 2,\n    VmHWM = 3,\n    VmRSS = 4,\n    VmSwap = 5,\n\n    VmMax = VmSwap + 1,\n};\n\nextern std::vector<size_t> getMemoryInfo();\nextern size_t getMemoryInfo(MemoryInfoType type);\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QKeyCommand.cc",
    "content": "#include \"QStore.h\"\n#include \"Log/Logger.h\"\n#include \"QGlobRegex.h\"\n#include <cassert>\n\nnamespace qedis\n{\n\nQError type(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const char* info = 0;\n    QType type = QSTORE.KeyType(params[1]);\n    switch (type) {\n        case QType_hash:\n            info = \"hash\";\n            break;\n            \n        case QType_set:\n            info = \"set\";\n            break;\n            \n        case QType_string:\n            info = \"string\";\n            break;\n            \n        case QType_list:\n            info = \"list\";\n            break;\n            \n        case QType_sortedSet:\n            info = \"sortedSet\";\n            break;\n            \n        default:\n            info = \"none\";\n            break;\n    }\n\n    FormatSingle(info, reply);\n    return QError_ok;\n}\n\nQError exists(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (QSTORE.ExistsKey(params[1]))\n        Format1(reply);\n    else\n        Format0(reply);\n\n    return QError_ok;\n}\n\nQError del(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    int nDel = 0;\n    for (size_t i = 1; i < params.size(); ++ i)\n    {\n        const QString& key = params[i];\n    \n        if (QSTORE.DeleteKey(key))\n        {\n            QSTORE.ClearExpire(key);\n            ++ nDel;\n        }\n    }\n    \n    FormatInt(nDel, reply);\n    return QError_ok;\n}\n\nstatic int _SetExpireByMs(const QString& key, uint64_t absTimeout)\n{\n    INF << \"try set expire, key \" << key.c_str() << \", timeout is \" << absTimeout;\n\n    int ret = 0;\n    if (QSTORE.ExistsKey(key))\n    {\n        QSTORE.SetExpire(key, absTimeout);\n        ret = 1;\n    }\n\n    return ret;\n}\n\nQError expire(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n    const uint64_t timeout = atoi(params[2].c_str()); // by seconds;\n        \n    int ret = _SetExpireByMs(key, ::Now() + timeout * 1000);\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError pexpire(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n    const uint64_t timeout = atoi(params[2].c_str()); // by milliseconds;\n        \n    int ret = _SetExpireByMs(key, ::Now() + timeout);\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError expireat(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n    const uint64_t timeout = atoi(params[2].c_str()); // by seconds;\n        \n    int ret = _SetExpireByMs(key, timeout * 1000);\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError pexpireat(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n    const uint64_t timeout = atoi(params[2].c_str()); // by milliseconds;\n        \n    int ret = _SetExpireByMs(key, timeout);\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\n\nstatic int64_t _ttl(const QString& key)\n{\n    int64_t ret = QStore::ExpireResult::notExist;\n    if (QSTORE.ExistsKey(key))\n    {\n        int64_t ttl = QSTORE.TTL(key, ::Now());\n        if (ttl < 0)\n            ret = QStore::ExpireResult::persist;\n        else\n            ret = ttl;\n    }\n    else\n    {\n        ERR << \"ttl not exist key:\" << key.c_str();\n    }\n\n    return  ret;\n}\n\nQError ttl(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n\n    int64_t ret = _ttl(key);\n    if (ret > 0)  ret /= 1000; // by seconds\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError pttl(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n\n    int64_t ret = _ttl(key); // by milliseconds\n    \n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError persist(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n\n    int ret = QSTORE.ClearExpire(key) ? 1 : 0;\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError move(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& key = params[1];\n    int toDb = atoi(params[2].c_str());\n\n    int ret = 0;\n\n    QObject* val;\n    if (QSTORE.GetValue(key, val) == QError_ok)\n    {\n        int fromDb = QSTORE.SelectDB(toDb);\n        if (fromDb >= 0 && fromDb != toDb && !QSTORE.ExistsKey(key))\n        {\n            QSTORE.SelectDB(toDb);\n            QSTORE.SetValue(key, std::move(*val)); // set to new db\n            \n            QSTORE.SelectDB(fromDb);\n            QSTORE.ClearExpire(key);\n            QSTORE.DeleteKey(key); // delete from old db\n            \n            ret = 1;\n            \n            INF << \"move \" << key << \" to db \" << toDb << \", from db \" << fromDb;\n        }\n        else\n        {\n            ERR << \"move \" << key << \" failed to db \" << toDb << \", from db \" << fromDb;\n        }\n    }\n    else\n    {\n        ERR << \"move \" << key << \" failed to db \" << toDb;\n    }\n\n    FormatInt(ret, reply);\n    return QError_ok;\n}\n\nQError keys(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& pattern = params[1];\n    \n    std::vector<const QString* > results;\n    for (const auto& kv : QSTORE)\n    {\n        if (glob_match(pattern, kv.first))\n            results.push_back(&kv.first);\n    }\n    \n    PreFormatMultiBulk(results.size(), reply);\n    for (auto e : results)\n    {\n        FormatBulk(*e, reply);\n    }\n\n    return   QError_ok;\n}\n\nQError randomkey(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    const QString& res = QSTORE.RandomKey();\n  \n    if (res.empty())\n        FormatNull(reply);\n    else\n        FormatBulk(res, reply);\n    \n    return QError_ok;\n}\n\nstatic QError RenameKey(const QString& oldKey, const QString& newKey, bool force)\n{\n    QObject* val;\n    \n    QError err = QSTORE.GetValue(oldKey, val);\n    if (err != QError_ok)\n        return err;\n    \n    if (!force && QSTORE.ExistsKey(newKey))\n        return QError_exist;\n    \n    auto now = ::Now();\n    auto ttl = QSTORE.TTL(oldKey, now);\n    \n    if (ttl == QStore::expired)\n        return QError_notExist;\n    \n    QSTORE.SetValue(newKey, std::move(*val));\n    if (ttl > 0)\n        QSTORE.SetExpire(newKey, ttl + now);\n    else if (ttl == QStore::persist)\n        QSTORE.ClearExpire(newKey);\n    \n    QSTORE.ClearExpire(oldKey);\n    QSTORE.DeleteKey(oldKey);\n    \n    return QError_ok;\n}\n\nQError rename(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QError err = RenameKey(params[1], params[2], true);\n    \n    ReplyError(err, reply);\n    return err;\n}\n\nQError renamenx(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QError err = RenameKey(params[1], params[2], false);\n    \n    if (err == QError_ok)\n        Format1(reply);\n    else\n        ReplyError(err, reply);\n    \n    return err;\n}\n\n// helper func scan\nstatic QError ParseScanOption(const std::vector<QString>& params, int start, long& count, const char*& pattern)\n{\n    // scan cursor  MATCH pattern  COUNT 1\n    count = -1;\n    pattern = nullptr;\n    for (std::size_t i = start; i < params.size(); i += 2)\n    {\n        if (params[i].size() == 5)\n        {\n            if (strncasecmp(params[i].c_str(), \"match\", 5) == 0)\n            {\n                if (!pattern)\n                {\n                    pattern = params[i + 1].c_str();\n                    continue;\n                }\n            }\n            else if (strncasecmp(params[i].c_str(), \"count\", 5) == 0)\n            {\n                if (count == -1)\n                {\n                    if (Strtol(params[i+1].c_str(), params[i+1].size(), &count))\n                        continue;\n                }\n            }\n        }\n        \n        return QError_param;\n    }\n\n    return QError_ok;\n}\n\nQError scan(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 != 0)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    long cursor = 0;\n\n    if (!Strtol(params[1].c_str(), params[1].size(), &cursor))\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    // scan cursor  MATCH pattern  COUNT 1\n    long count  = -1;\n    const char* pattern = nullptr;\n    \n    QError err = ParseScanOption(params, 2, count, pattern);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    if (count < 0) count = 5;\n    \n    std::vector<QString>  res;\n    auto newCursor = QSTORE.ScanKey(cursor, count, res);\n  \n    // filter by pattern\n    if (pattern)\n    {\n        for (auto it = res.begin(); it != res.end(); )\n        {\n            if (!glob_match(pattern, (*it).c_str()))\n                it = res.erase(it);\n            else\n                ++ it;\n        }\n    }\n    \n    // reply\n    PreFormatMultiBulk(2, reply);\n\n    char buf[32];\n    auto len = snprintf(buf, sizeof buf -1, \"%lu\", newCursor);\n    FormatBulk(buf, len, reply);\n\n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& s : res)\n    {\n        FormatBulk(s, reply);\n    }\n    \n    return QError_ok;\n}\n\n\nQError hscan(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // hscan key cursor COUNT 0 MATCH 0\n    if (params.size() % 2 == 0)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    long cursor = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &cursor))\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    // find hash\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_hash);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    // parse option\n    long count = -1;\n    const char* pattern = nullptr;\n    \n    err = ParseScanOption(params, 3, count, pattern);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    if (count < 0)  count = 5;\n    \n    // scan\n    std::vector<QString>  res;\n    auto newCursor = HScanKey(*value->CastHash(), cursor, count, res);\n    \n    // filter by pattern\n    if (pattern)\n    {\n        for (auto it = res.begin(); it != res.end(); )\n        {\n            if (!glob_match(pattern, (*it).c_str()))\n            {\n                it = res.erase(it); // erase key\n                it = res.erase(it); // erase value\n            }\n            else\n            {\n                ++ it, ++ it;\n            }\n        }\n    }\n    \n    // reply\n    PreFormatMultiBulk(2, reply);\n    \n    char buf[32];\n    auto len = snprintf(buf, sizeof buf -1, \"%lu\", newCursor);\n    FormatBulk(buf, len, reply);\n    \n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& s : res)\n    {\n        FormatBulk(s, reply);\n    }\n    \n    return QError_ok;\n}\n\n\nQError sscan(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // sscan key cursor COUNT 0 MATCH 0\n    if (params.size() % 2 == 0)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    long cursor = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &cursor))\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    // find set\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_set);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    // parse option\n    long count  = -1;\n    const char* pattern = nullptr;\n    \n    err = ParseScanOption(params, 3, count, pattern);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    if (count < 0)  count = 5;\n    \n    // scan\n    std::vector<QString> res;\n    auto newCursor = SScanKey(*value->CastSet(), cursor, count, res);\n    \n    // filter by pattern\n    if (pattern)\n    {\n        for (auto it = res.begin(); it != res.end(); )\n        {\n            if (!glob_match(pattern, (*it).c_str()))\n            {\n                it = res.erase(it);\n            }\n            else\n            {\n                ++ it;\n            }\n        }\n    }\n    \n    // reply\n    PreFormatMultiBulk(2, reply);\n    \n    char buf[32];\n    auto len = snprintf(buf, sizeof buf -1, \"%lu\", newCursor);\n    FormatBulk(buf, len, reply);\n    \n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& s : res)\n    {\n        FormatBulk(s, reply);\n    }\n    \n    return   QError_ok;\n}\n    \nQError sort(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // sort key desc/asc alpha\n    QObject* value;\n    QError err = QSTORE.GetValue(params[1], value);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    if (value->type != QType_list &&\n        value->type != QType_set &&\n        value->type != QType_sortedSet)\n    {\n        ReplyError(QError_type, reply);\n        return QError_type;\n    }\n    \n    bool asc = true;\n    bool alpha = false;\n    for (const auto& arg : params)\n    {\n        if (strncasecmp(arg.data(), \"desc\", 4) == 0)\n            asc = false;\n        else if (strncasecmp(arg.data(), \"alpha\", 5) == 0)\n            alpha = true;\n    }\n    \n    std::vector<const QString* > values;\n    switch (value->type)\n    {\n        case QType_list:\n        {\n            PLIST l = value->CastList();\n            std::for_each(l->begin(), l->end(), [&](const QString& v) {\n                values.push_back(&v);\n            });\n        }\n            break;\n            \n        case QType_set:\n        {\n            PSET s = value->CastSet();\n            std::for_each(s->begin(), s->end(), [&](const QString& v) {\n                values.push_back(&v);\n            });\n        }\n            break;\n            \n        case QType_sortedSet:\n        {\n            // TODO\n            FormatOK(reply);\n            return QError_ok;\n        }\n            break;\n            \n        default:\n            break;\n    }\n\n    std::sort(values.begin(), values.end(), [=](const QString* a, const QString* b)->bool {\n        if (!alpha)\n        {\n            long avalue = 0, bvalue = 0;\n            TryStr2Long(a->data(), a->size(), avalue);\n            TryStr2Long(b->data(), b->size(), bvalue);\n            \n            if (asc)\n                return avalue < bvalue;\n            else\n                return bvalue < avalue;\n        }\n        else\n        {\n            if (asc)\n                return std::lexicographical_compare(a->begin(), a->end(),\n                                                    b->begin(), b->end());\n            else\n                return std::lexicographical_compare(b->begin(), b->end(),\n                                                    a->begin(), a->end());\n        }\n    });\n    \n    PreFormatMultiBulk(values.size(), reply);\n    for (const auto v : values)\n    {\n        FormatBulk(*v, reply);\n    }\n    \n    return QError_ok;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QLeveldb.cc",
    "content": "\n#include \"QLeveldb.h\"\n#include \"leveldb/db.h\"\n#include \"Log/Logger.h\"\n#include \"UnboundedBuffer.h\"\n\nnamespace qedis\n{\n\nQLeveldb::QLeveldb() : db_(nullptr)\n{\n}\n\nQLeveldb::~QLeveldb()\n{\n    delete db_;\n}\n\nbool QLeveldb::IsOpen() const \n{\n    return db_ != nullptr;\n}\n\nbool QLeveldb::Open(const char* path)\n{\n    leveldb::Options options;\n    options.create_if_missing = true; \n    // options.error_if_exists = true; \n                    \n    auto s = leveldb::DB::Open(options, path, &db_); \n    if (!s.ok()) {\n        ERR << \"Open db_ failed:\" << s.ToString(); \n    }\n\n    return s.ok();\n}\n\nQObject QLeveldb::Get(const QString& key)\n{\n    std::string value;\n    auto status = db_->Get(leveldb::ReadOptions(), leveldb::Slice(key.data(), key.size()), &value);\n    if (!status.ok())\n        return QObject(QType_invalid);\n\n    int64_t remainTtlSeconds = 0;\n    QObject obj = _DecodeObject(value.data(), value.size(), remainTtlSeconds);\n    // trick: use obj.lru to store the remain seconds to be expired.\n    if (remainTtlSeconds > 0)\n        obj.lru = static_cast<uint32_t>(remainTtlSeconds);\n    else\n        obj.lru = 0;\n\n    return obj;\n}\n\nbool QLeveldb::Put(const QString& key)\n{\n    QObject* obj;\n    QError ok = QSTORE.GetValue(key, obj, false);\n    if (ok != QError_ok)\n        return false;\n\n    uint64_t now = ::Now();\n    int64_t ttl = QSTORE.TTL(key, now);\n    if (ttl > 0)\n        ttl += now;\n    else if (ttl == QStore::ExpireResult::expired)\n        return false;\n\n    return Put(key, *obj, ttl);\n}\n\nbool QLeveldb::Put(const QString& key, const QObject& obj, int64_t absttl)\n{\n    UnboundedBuffer v;\n    _EncodeObject(obj, absttl, v);\n\n    leveldb::Slice lkey(key.data(), key.size());\n    leveldb::Slice lval(v.ReadAddr(), v.ReadableSize());\n\n    auto s = db_->Put(leveldb::WriteOptions(), lkey, lval); \n    return s.ok();\n}\n\nbool QLeveldb::Delete(const QString& key)\n{\n    leveldb::Slice lkey(key.data(), key.size());\n    auto s = db_->Delete(leveldb::WriteOptions(), lkey); \n    return s.ok();\n}\n\n\nvoid QLeveldb::_EncodeObject(const QObject& obj, int64_t absttl, UnboundedBuffer& v)\n{\n    // value format: | ttl flag 1byte| ttl 8bytes if has|type 1byte| object contents\n\n    // write ttl, if has\n    int8_t ttlflag = (absttl > 0 ? 1 : 0);\n    v.Write(&ttlflag, sizeof ttlflag);\n    if (ttlflag)\n        v.Write(&absttl, sizeof absttl);\n\n    // write type\n    int8_t type = obj.type;\n    v.Write(&type, sizeof type);\n\n    switch (obj.encoding)\n    {\n        case QEncode_raw:\n        case QEncode_int:\n            {\n                auto str = GetDecodedString(&obj);\n                _EncodeString(*str, v);\n            }\n            break;\n    \n        case QEncode_list:\n            _EncodeList(obj.CastList(), v);\n            break;\n            \n        case QEncode_set:\n            _EncodeSet(obj.CastSet(), v);\n            break;\n            \n        case QEncode_hash:\n            _EncodeHash(obj.CastHash(), v);\n            break;\n            \n        case QEncode_sset:\n            _EncodeSSet(obj.CastSortedSet(), v);\n            break;\n            \n        default:\n            break;\n    }\n}\n\n\nvoid QLeveldb::_EncodeString(const QString& str, UnboundedBuffer& v)\n{\n    // write size\n    auto len = static_cast<uint32_t>(str.size());\n    v.Write(&len, 4);\n    // write content\n    v.Write(str.data(), len);\n}\n     \nvoid QLeveldb::_EncodeHash(const PHASH& h, UnboundedBuffer& v)\n{\n    // write size\n    auto len = static_cast<uint32_t>(h->size());\n    v.Write(&len, 4);\n\n    for (const auto& e : *h)\n    {\n        _EncodeString(e.first, v);\n        _EncodeString(e.second, v);\n    }\n}\n     \nvoid QLeveldb::_EncodeList(const PLIST& l, UnboundedBuffer& v)\n{\n    // write size\n    auto len = static_cast<uint32_t>(l->size());\n    v.Write(&len, 4);\n\n    for (const auto& e : *l)\n    {\n        _EncodeString(e, v);\n    }\n}\n\nvoid QLeveldb::_EncodeSet(const PSET& s, UnboundedBuffer& v)\n{\n    auto len = static_cast<uint32_t>(s->size());\n    v.Write(&len, 4);\n\n    for (const auto& e : *s)\n    {\n        _EncodeString(e, v);\n    }\n}\n\nvoid QLeveldb::_EncodeSSet(const PSSET& ss, UnboundedBuffer& v)\n{\n    auto len = static_cast<uint32_t>(ss->Size());\n    v.Write(&len, 4);\n\n    for (const auto& e : *ss)\n    {\n        _EncodeString(e.first, v);\n    \n        auto s(std::to_string(e.second));\n        _EncodeString(s, v);\n    }\n}\n\nQObject QLeveldb::_DecodeObject(const char* data, size_t len, int64_t& remainTtl)\n{\n    // | type 1byte | ttl flag 1byte| ttl 8bytes, if has|\n\n    remainTtl = 0;\n\n    size_t offset = 0;\n\n    int8_t hasttl = *(int8_t*)(data + offset);\n    offset += sizeof hasttl;\n\n    int64_t absttl = 0;\n    if (hasttl)\n    {\n        absttl = *(int64_t*)(data + offset);\n        offset += sizeof absttl;\n    }\n\n    if (absttl != 0)\n    {\n        int64_t now = static_cast<int64_t>(::Now());\n        if (absttl <= now)\n        {\n            DBG << \"Load from leveldb is timeout \" << absttl;\n            return QObject(QType_invalid);\n        }\n        else\n        {\n            // Only support seconds, because lru is 24bits, too short.\n            remainTtl = (absttl - now) / 1000;\n            INF << \"Load from leveldb remainTtlSeconds: \" << remainTtl;\n        }\n    }\n\n    int8_t type = *(int8_t*)(data + offset);\n    offset += sizeof type;\n\n    switch (type)\n    {\n        case QType_string:\n        {\n            return QObject::CreateString(_DecodeString(data + offset, len - offset));\n        }\n        case QType_list:\n        {\n            return _DecodeList(data + offset, len - offset);\n        }\n        case QType_set:\n        {\n            return _DecodeSet(data + offset, len - offset);\n        }\n        case QType_sortedSet:\n        {\n            return _DecodeSSet(data + offset, len - offset);\n        }\n        case QType_hash:\n        {\n            return _DecodeHash(data + offset, len - offset);\n        }\n            \n        default:\n            break;\n    }\n    \n    assert(false);\n    return QObject(QType_invalid);\n}\n\nQString QLeveldb::_DecodeString(const char* data, size_t len)\n{\n    assert(len > 4);\n    // read length\n    uint32_t slen = *(uint32_t*)(data);\n    // read content\n    const char* sdata = data + 4;\n\n    return QString(sdata, slen);\n}\n\nQObject QLeveldb::_DecodeHash(const char* data, size_t len)\n{\n    assert(len >= 4);\n    uint32_t hlen = *(uint32_t*)(data);\n\n    QObject obj(QObject::CreateHash());\n    PHASH hash(obj.CastHash());\n\n    size_t offset = 4;\n    for (uint32_t i = 0; i < hlen; ++ i)\n    {\n        auto key = _DecodeString(data + offset, len - offset);\n        offset += key.size() + 4;\n\n        auto value = _DecodeString(data + offset, len - offset);\n        offset += value.size() + 4;\n\n        hash->insert(QHash::value_type(key, value));\n        DBG << \"Load from leveldb: hash key : \" << key << \" val : \" << value;\n    }\n\n    return obj;\n}\n\nQObject QLeveldb::_DecodeList(const char* data, size_t len)\n{\n    assert(len >= 4);\n    uint32_t llen = *(uint32_t*)(data);\n\n    QObject obj(QObject::CreateList());\n    PLIST list(obj.CastList());\n\n    size_t offset = 4;\n    for (uint32_t i = 0; i < llen; ++ i)\n    {\n        auto elem = _DecodeString(data + offset, len - offset);\n        offset += elem.size() + 4;\n\n        list->push_back(elem);\n        DBG << \"Load list elem from leveldb: \" << elem;\n    }\n\n    return obj;\n}\n     \nQObject QLeveldb::_DecodeSet(const char* data, size_t len)\n{\n    assert(len >= 4);\n    uint32_t slen = *(uint32_t*)(data);\n\n    QObject obj(QObject::CreateSet());\n    PSET set(obj.CastSet());\n\n    size_t offset = 4;\n    for (uint32_t i = 0; i < slen; ++ i)\n    {\n        auto elem = _DecodeString(data + offset, len - offset);\n        offset += elem.size() + 4;\n\n        set->insert(elem);\n        DBG << \"Load set elem from leveldb: \" << elem;\n    }\n\n    return obj;\n}\n\nQObject QLeveldb::_DecodeSSet(const char* data, size_t len)\n{\n    assert(len >= 4);\n    uint32_t sslen = *(uint32_t*)(data);\n\n    QObject obj(QObject::CreateSSet());\n    PSSET sset(obj.CastSortedSet());\n\n    size_t offset = 4;\n    for (uint32_t i = 0; i < sslen; ++ i)\n    {\n        auto member = _DecodeString(data + offset, len - offset);\n        offset += member.size() + 4;\n\n        auto scoreStr = _DecodeString(data + offset, len - offset);\n        offset += scoreStr.size() + 4;\n\n        double score = std::stod(scoreStr);\n        sset->AddMember(member, static_cast<long>(score));\n\n        DBG << \"Load leveldb sset member : \" << member << \" score : \" << score;\n    }\n\n    return obj;\n}\n     \n}\n\n"
  },
  {
    "path": "QedisCore/QLeveldb.h",
    "content": "#ifndef BERT_QLEVELDB_H\n#define BERT_QLEVELDB_H\n\n#include \"QDumpInterface.h\"\n#include \"QStore.h\"\n\nnamespace leveldb\n{\nclass DB;\n}\n\nnamespace qedis\n{\n\nclass UnboundedBuffer;\n\nclass QLeveldb : public QDumpInterface\n{\npublic:\n    QLeveldb();\n    ~QLeveldb();\n\n    bool Open(const char* path);\n    bool IsOpen() const ;\n\n    QObject Get(const QString& key) override;\n    bool Put(const QString& key) override;\n\n    bool Put(const QString& key, const QObject& obj, int64_t ttl = 0) override;\n    bool Delete(const QString& key) override;\n\nprivate:\n     leveldb::DB* db_;\n\n     // encoding stuff\n\n     // value format: type + ttl(if has) + qobject\n     void _EncodeObject(const QObject& obj, int64_t absttl, UnboundedBuffer& v);\n\n     void _EncodeString(const QString& str, UnboundedBuffer& v);\n     void _EncodeHash(const PHASH& , UnboundedBuffer& v);\n     void _EncodeList(const PLIST& , UnboundedBuffer& v);\n     void _EncodeSet(const PSET& , UnboundedBuffer& v);\n     void _EncodeSSet(const PSSET& , UnboundedBuffer& v);\n\n     // decoding stuff\n     QObject _DecodeObject(const char* data, size_t len, int64_t& remainTtlSeconds);\n\n     QString _DecodeString(const char* data, size_t len);\n     QObject _DecodeHash(const char* data, size_t len);\n     QObject _DecodeList(const char* data, size_t len);\n     QObject _DecodeSet(const char* data, size_t len);\n     QObject _DecodeSSet(const char* data, size_t len);\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QList.cc",
    "content": "#include \"QList.h\"\n#include \"QStore.h\"\n#include \"QClient.h\"\n#include \"Log/Logger.h\"\n#include <algorithm>\n#include <cassert>\n\nusing std::vector;\n\nnamespace qedis\n{\n\nQObject QObject::CreateList()\n{\n    QObject list(QType_list);\n    list.Reset(new QList);\n\n    return list;\n}\n\nstatic QError push(const vector<QString>& params, UnboundedBuffer* reply, ListPosition pos, bool createIfNotExist = true)\n{\n    QObject* value;\n    \n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        if (err != QError_notExist)\n        {\n            ReplyError(err, reply);\n            return err;\n        }\n        else if (createIfNotExist)\n        {\n            value = QSTORE.SetValue(params[1], QObject::CreateList());\n        }\n        else\n        {\n            ReplyError(err, reply);\n            return err;\n        }\n    }\n\n    auto list = value->CastList();\n    bool mayReady = list->empty();\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        if (pos == ListPosition::head)\n            list->push_front(params[i]);\n        else\n            list->push_back(params[i]);\n    }\n    \n    FormatInt(static_cast<long>(list->size()), reply);\n    if (mayReady && !list->empty())\n    {\n        if (reply) // Do not propogate if aof reload...\n        {\n            // push must before pop(serve)...\n            Propogate(params);                   // the push\n            QSTORE.ServeClient(params[1], list); // the pop\n        }\n        return QError_nop;\n    }\n    else\n    {\n        return QError_ok;\n    }\n}\n\n\nstatic QError GenericPop(const QString& key, ListPosition pos, QString& result)\n{\n    QObject* value;\n    \n    QError err = QSTORE.GetValueByType(key, value, QType_list);\n    if (err != QError_ok)\n    {\n        return  err;\n    }\n    \n    auto list = value->CastList();\n    assert (!list->empty());\n\n    if (pos == ListPosition::head)\n    {\n        result = std::move(list->front());\n        list->pop_front();\n    }\n    else\n    {\n        result = std::move(list->back());\n        list->pop_back();\n    }\n    \n    if (list->empty())\n    {\n        QSTORE.DeleteKey(key);\n    }\n    \n    return QError_ok;\n}\n\nQError lpush(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    return push(params, reply, ListPosition::head);\n}\n\nQError rpush(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    return push(params, reply, ListPosition::tail);\n}\n\nQError lpushx(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    return push(params, reply, ListPosition::head, false);\n}\n\nQError rpushx(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    return push(params, reply, ListPosition::tail, false);\n}\n\nQError lpop(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QString result;\n    QError err = GenericPop(params[1], ListPosition::head, result);\n    switch (err)\n    {\n        case QError_ok:\n            FormatBulk(result, reply);\n            break;\n            \n        default:\n            ReplyError(err, reply);\n            break;\n    }\n    \n    return err;\n}\n\nQError rpop(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QString result;\n    QError err = GenericPop(params[1], ListPosition::tail, result);\n    switch (err)\n    {\n        case QError_ok:\n            FormatBulk(result, reply);\n            break;\n            \n        default:\n            ReplyError(err, reply);\n            break;\n    }\n    \n    return  err;\n}\n\nstatic bool _BlockClient( QClient* client, const QString& key, uint64_t timeout, ListPosition pos, const QString* dstList = 0)\n{\n    auto now = ::Now();\n    \n    if (timeout > 0)\n        timeout += now;\n    else\n        timeout = std::numeric_limits<uint64_t>::max();\n    \n    return QSTORE.BlockClient(key, client, timeout, pos, dstList);\n\n}\n\nstatic QError  _GenericBlockedPop(vector<QString>::const_iterator keyBegin,\n                                  vector<QString>::const_iterator keyEnd,\n                                  UnboundedBuffer* reply,\n                                  ListPosition  pos, long timeout,\n                                  const QString* target = nullptr,\n                                  bool withKey = true)\n{\n    for (auto it(keyBegin); it != keyEnd; ++ it)\n    {\n        QString result;\n        QError err = GenericPop(*it, pos, result);\n        \n        switch (err)\n        {\n            case QError_ok:\n                if (withKey)\n                {\n                    PreFormatMultiBulk(2, reply);\n                    FormatBulk(*it, reply);\n                }\n                FormatBulk(result, reply);\n                \n                if (target)\n                {\n                    // the target process\n                }\n\n                {\n                    std::vector<QString> params;\n                    params.push_back(pos == ListPosition::head ? \"lpop\" : \"rpop\");\n                    params.push_back(*it);\n\n                    QClient::Current()->RewriteCmd(params);\n                }\n                return err;\n                \n            case QError_type:\n                ReplyError(err, reply);\n                return err;\n                \n            case QError_notExist:\n                break;\n                \n            default:\n                assert(!!!\"Unknow error\");\n        }\n    }\n    \n    // Do NOT block if in transaction\n    if (QClient::Current() && QClient::Current()->IsFlagOn(ClientFlag_multi))\n    {\n        FormatNull(reply);\n        return QError_nop;\n    }\n    \n    // Put client to the wait-list\n    for (auto it(keyBegin); it != keyEnd; ++ it)\n    {\n        _BlockClient(QClient::Current(), *it, timeout, pos, target);\n    }\n    \n    return QError_nop;\n}\n\nQError blpop(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    long timeout;\n    if (!TryStr2Long(params.back().c_str(),\n                     params.back().size(),\n                     timeout))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    timeout *= 1000;\n    \n    return  _GenericBlockedPop(++ params.begin(), -- params.end(),\n                               reply, ListPosition::head, timeout);\n}\n\nQError  brpop(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    long timeout;\n    if (!TryStr2Long(params.back().c_str(),\n                     params.back().size(),\n                     timeout))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    timeout *= 1000;\n    \n    return  _GenericBlockedPop(++ params.begin(), -- params.end(),\n                               reply, ListPosition::tail, timeout);\n}\n\nQError  lindex(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        FormatNull(reply);\n        return  err;\n    }\n    \n    long idx;\n    if (!TryStr2Long(params[2].c_str(), params[2].size(), idx))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    auto list = value->CastList();\n    const int size = static_cast<int>(list->size());\n    if (idx < 0)\n        idx += size;\n    \n    if (idx < 0 || idx >= size)\n    {\n        FormatNull(reply);\n        return  QError_ok;\n    }\n    \n    const QString* result = nullptr;\n    \n    if (2 * idx < size)\n    {\n        auto it = list->begin();\n        std::advance(it, idx);\n        result = &*it;\n    }\n    else\n    {\n        auto it = list->rbegin();\n        idx = size - 1 - idx;\n        std::advance(it, idx);\n        result = &*it;\n    }\n    \n    FormatBulk(*result, reply);\n    return QError_ok;\n}\n\n\nQError lset(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        ReplyError(QError_notExist, reply);\n        return err;\n    }\n    \n    auto list = value->CastList();\n    long idx;\n    if (!TryStr2Long(params[2].c_str(), params[2].size(), idx))\n    {\n        ReplyError(QError_param, reply);\n        return  QError_notExist;\n    }\n    \n    const int size = static_cast<int>(list->size());\n    if (idx < 0)\n        idx += size;\n    \n    if (idx < 0 || idx >= size)\n    {\n        FormatNull(reply);\n        return  QError_ok;\n    }\n    \n    QString* result = nullptr;\n    \n    if (2 * idx < size)\n    {\n        auto it = list->begin();\n        std::advance(it, idx);\n        result = &*it;\n    }\n    else\n    {\n        auto it = list->rbegin();\n        idx = size - 1 - idx;\n        std::advance(it, idx);\n        result = &*it;\n    }\n    \n    *result = params[3];\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\n\nQError llen(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        if (err == QError_type)\n            ReplyError(err, reply);\n        else\n            Format0(reply);\n\n        return  err;\n    }\n    \n    auto list = value->CastList();\n    FormatInt(static_cast<long>(list->size()), reply);\n    return QError_ok;\n}\n\nstatic void Index2Iterator(long start, long end,\n                           QList&  list,\n                           QList::iterator* beginIt,\n                           QList::iterator* endIt)\n{\n    assert (start >= 0 && end >= 0 && start <= end);\n    assert (end < static_cast<long>(list.size()));\n    \n    long size = static_cast<long>(list.size());\n    if (beginIt)\n    {\n        if (start * 2 < size)\n        {\n            *beginIt = list.begin();\n            while (start -- > 0)   ++ *beginIt;\n        }\n        else\n        {\n            *beginIt = list.end();\n            while (start ++ < size)  -- *beginIt;\n        } \n    } \n    \n    if (endIt)\n    {\n        if (end * 2 < size)\n        {\n            *endIt = list.begin();\n            while (end -- > 0)   ++ *endIt;\n        }\n        else\n        {\n            *endIt = list.end();\n            while (end ++ < size)  -- *endIt;\n        }\n    }\n}\n\nstatic size_t GetRange(long start, long end,\n                       QList&  list,\n                       QList::iterator* beginIt = nullptr,\n                       QList::iterator* endIt = nullptr)\n{\n    size_t rangeLen = 0;\n    if (start > end)  // empty\n    {\n        if (beginIt)    *beginIt = list.end();\n        if (endIt)      *endIt = list.end();\n    }\n    else if (start != 0 || end + 1 != static_cast<long>(list.size()))\n    {\n        rangeLen = end - start + 1;\n        Index2Iterator(start, end, list, beginIt, endIt);\n    }\n    else\n    {\n        rangeLen = list.size();\n        if (beginIt) *beginIt = list.begin();\n        if (endIt)   *endIt   = -- list.end();  // entire list\n    }\n    \n    return rangeLen;\n}\n\n\nQError  ltrim(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return  err;\n    }\n    \n    long start, end;\n    if (!Strtol(params[2].c_str(), params[2].size(), &start) ||\n        !Strtol(params[3].c_str(), params[3].size(), &end))\n    {\n        ReplyError(QError_param, reply);\n        return err;\n    }\n    \n    auto list = value->CastList();\n    AdjustIndex(start, end, list->size());\n    \n    QList::iterator beginIt, endIt;\n    GetRange(start, end, *list, &beginIt, &endIt);\n    \n    if (beginIt != list->end())\n    {\n        assert (endIt != list->end());\n        list->erase(list->begin(), beginIt);\n        list->erase(++ endIt, list->end());\n    }\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError lrange(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return err;\n    }\n    \n    long start, end;\n    if (!Strtol(params[2].c_str(), params[2].size(), &start) ||\n        !Strtol(params[3].c_str(), params[3].size(), &end))\n    {\n        ReplyError(QError_param, reply);\n        return err;\n    }\n    \n    auto list = value->CastList();\n    AdjustIndex(start, end, list->size());\n    \n    QList::iterator beginIt;\n    size_t rangeLen = GetRange(start, end, *list, &beginIt);\n    \n    PreFormatMultiBulk(rangeLen, reply);\n    if (beginIt != list->end())\n    {\n        while (rangeLen != 0)\n        {\n            FormatBulk(beginIt->c_str(), beginIt->size(), reply);\n            ++ beginIt;\n            -- rangeLen;\n        }\n    }\n    \n    return QError_ok;\n}\n\nQError linsert(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return err;\n    }\n    \n    bool before = false;\n    if (params[2] == \"before\")\n        before = true;\n    else if (params[2] == \"after\")\n        before = false;\n    else\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    auto list = value->CastList();\n    QList::iterator it = std::find(list->begin(), list->end(), params[3]);\n    if (it == list->end())\n    {\n        FormatInt(-1, reply);\n        return QError_notExist;\n    }\n    \n    if (before)\n        list->insert(it, params[4]);\n    else\n        list->insert(++ it, params[4]);\n    \n    FormatInt(static_cast<long>(list->size()), reply);\n    return QError_ok;\n}\n\n\nQError  lrem(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_list);\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return err;\n    }\n    \n    long count;\n    if (!Strtol(params[2].c_str(), params[2].size(), &count))\n    {\n        ReplyError(QError_param, reply);\n        return err;\n    }\n    \n    auto list = value->CastList();\n    ListPosition  start = ListPosition::head;\n    if (count < 0)\n    {\n        count = -count;\n        start = ListPosition::tail;\n    }\n    else if (count == 0)\n    {\n        count = list->size(); // remove all elements equal to param[3]\n    }\n    \n    long resultCount = 0;\n    if (start == ListPosition::head)\n    {\n        auto it = list->begin();\n        while (it != list->end() && resultCount < count)\n        {\n            if (*it == params[3])\n            {\n                list->erase(it ++);\n                ++ resultCount;\n            }\n            else\n            {\n                ++ it;\n            }\n        }\n    }\n    else\n    {\n        auto it = list->rbegin();\n        while (it != list->rend() && resultCount < count)\n        {\n            if (*it == params[3])\n            {\n                list->erase((++it).base()); // Effective STL, item 28\n                ++ resultCount;\n            }\n            else\n            {\n                ++ it;\n            }\n        }\n    }\n\n    FormatInt(resultCount, reply);\n    return QError_ok;\n}\n\nQError rpoplpush(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* src;\n    QError err = QSTORE.GetValueByType(params[1], src, QType_list);\n    if (err != QError_ok)\n    {\n        FormatNull(reply);\n        return err;\n    }\n    \n    auto srclist = src->CastList();\n    assert (!srclist->empty());\n    \n    QObject* dst;\n    err = QSTORE.GetValueByType(params[2], dst, QType_list);\n    if (err != QError_ok)\n    {\n        if (err != QError_notExist)\n        {\n            ReplyError(err, reply);\n            return err;\n        }\n\n        dst = QSTORE.SetValue(params[2], QObject::CreateList());\n    }\n    \n    auto dstlist = dst->CastList();\n    dstlist->splice(dstlist->begin(), *srclist, (++ srclist->rbegin()).base());\n    \n    FormatBulk(*(dstlist->begin()), reply);\n    return QError_ok;\n}\n\nQError brpoplpush(const vector<QString>& params, UnboundedBuffer* reply)\n{\n    // check timeout format\n    long timeout;\n    if (!TryStr2Long(params.back().c_str(),\n                     params.back().size(),\n                     timeout))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    timeout *= 1000;\n    \n    // check target list\n    QObject* dst;\n    QError err = QSTORE.GetValueByType(params[2], dst, QType_list);\n    if (err != QError_ok)\n    {\n        if (err != QError_notExist)\n        {\n            ReplyError(err, reply);\n            return err;\n        }\n    }\n    \n    auto dstKeyIter = -- (-- params.end());\n    return  _GenericBlockedPop(++ params.begin(), dstKeyIter,\n                               reply, ListPosition::tail, timeout, &*dstKeyIter, false);\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QList.h",
    "content": "#ifndef BERT_QLIST_H\n#define BERT_QLIST_H\n\n#include \"QString.h\"\n#include <list>\n\nnamespace qedis\n{\n\nenum class ListPosition\n{\n    head,\n    tail,\n};\n\nusing QList = std::list<QString>;\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QMigration.cc",
    "content": "#include \"Server.h\"\n#include \"QMigration.h\"\n#include \"Timer.h\"\n#include \"QDB.h\"\n\nnamespace qedis\n{\n\nstatic void ProcessItem(MigrationItem& item, QMigrateClient* conn)\n{\n    UnboundedBuffer request;\n    for (auto it = item.keys.begin();\n              it != item.keys.end(); )\n    {\n        const auto& key = *it;\n        if (key.empty())\n        {\n            it = item.keys.erase(it);\n            continue;\n        }\n\n        const QObject* obj = QSTORE.GetObject(key);\n        if (!obj)\n        {\n            it = item.keys.erase(it);\n            continue;\n        }\n\n        std::string contents = DumpObject(*obj);\n        if (item.replace)\n        {\n            PreFormatMultiBulk(5, &request);\n            FormatMultiBulk({\"restore\", key, \"0\", contents, \"replace\"}, &request);\n        }\n        else\n        {\n            PreFormatMultiBulk(4, &request);\n            FormatMultiBulk({\"restore\", key, \"0\", contents}, &request);\n        }\n\n        ++ it;\n    }\n\n    if (!request.IsEmpty())\n    {\n        UnboundedBuffer select;\n        PreFormatMultiBulk(2, &select);\n        FormatMultiBulk({\"select\", std::to_string(item.dstDb)}, &select);\n\n        conn->SendPacket(select.ReadAddr(), select.ReadableSize());\n        conn->SendPacket(request.ReadAddr(), request.ReadableSize());\n\n        item.state = MigrateState::Processing;\n    }\n    else\n    {\n        item.state = MigrateState::Done;\n        auto c = item.client.lock();\n        if (c)\n            c->SendPacket(\"+NOKEY\\r\\n\", 8);\n    }\n}\n\n\nQMigrationManager& QMigrationManager::Instance()\n{\n    static QMigrationManager mgr;\n    return mgr;\n}\n    \nvoid QMigrationManager::Add(const MigrationItem& item)\n{\n    items_[item.dst].push_back(item);\n}\n\nvoid QMigrationManager::Add(MigrationItem&& item)\n{\n    items_[item.dst].push_back(std::move(item));\n}\n\nvoid QMigrationManager::InitMigrationTimer()\n{\n    auto timer = TimerManager::Instance().CreateTimer();\n    timer->Init(20);\n    timer->SetCallback([]() {\n       QMigrationManager::Instance().LoopCheck();\n    });\n    \n    TimerManager::Instance().AddTimer(timer);\n}\n\nvoid QMigrationManager::LoopCheck()\n{\n    auto now = ::time(nullptr);\n\n    for (auto iter(items_.begin()); iter != items_.end(); )\n    {\n        auto& items = iter->second;\n\n        for (auto it = items.begin(); it != items.end(); )\n        {\n            auto& item = *it;\n            if (item.state != MigrateState::Done && now > item.timeout)\n                item.state = MigrateState::Timeout;\n            \n            bool erased = false;\n            switch (item.state)\n            {\n                case MigrateState::None:\n                    {\n                        auto c = GetConnection(item.dst);\n                        if (c)\n                            ProcessItem(item, c.get());\n                    }\n                    break;\n\n                case MigrateState::Processing:\n                    //waiting....\n                    break;\n\n                case MigrateState::Timeout:\n                    {\n                        auto c = item.client.lock();\n                        if (c)\n                        {\n                            const char err[] = \"-IOERR error or timeout for target instance\\r\\n\";\n                            c->SendPacket(err, sizeof err - 1);\n                            item.state = MigrateState::Done;\n                        }\n                    }\n        \n                case MigrateState::Done:\n                    it = items.erase(it);\n                    erased = true;\n                    break;\n\n                default:\n                    assert(false);\n                    break;\n            }\n\n            if (!erased)\n                ++ it;\n        }\n\n        if (items.empty())\n            iter = items_.erase(iter);\n        else\n            ++ iter;\n    }\n}\n\nstd::shared_ptr<QMigrateClient>\nQMigrationManager::GetConnection(const SocketAddr& dst)\n{\n    auto it = conns_.find(dst);\n    if (it != conns_.end())\n    {\n        auto c = it->second.lock();\n        if (c)\n            return c;\n        else\n            conns_.erase(it);\n\n    }\n\n    if (pendingConnect_.count(dst))\n        return nullptr; // already connecting\n\n    pendingConnect_.insert(dst);\n    Server::Instance()->TCPConnect(dst,\n                                   std::bind(&QMigrationManager::OnConnectMigrateFail, this, dst),\n                                   ConnectionTag::kMigrateClient);\n\n    return nullptr;\n}\n\nvoid QMigrationManager::OnConnectMigrateFail(const SocketAddr& dst)\n{\n    pendingConnect_.erase(dst);\n    auto it = items_.find(dst);\n    if (it == items_.end())\n        return;\n\n    for (auto& item : it->second)\n    {\n        if (item.state == MigrateState::Done)\n            continue;\n\n        // can not ensure success, so retry\n        if (item.state == MigrateState::Processing)\n            item.state = MigrateState::None;\n    }\n}\n\nvoid QMigrationManager::OnConnect(QMigrateClient* client)\n{\n    const auto& dst = client->GetPeerAddr();\n    std::weak_ptr<QMigrateClient> wc = std::static_pointer_cast<QMigrateClient>(client->shared_from_this());\n\n    bool succ = conns_.insert({dst, wc}).second;\n    assert (succ);\n\n    size_t tmp = pendingConnect_.erase(dst);\n    assert (tmp == 1);\n\n    auto it = items_.find(dst);\n    if (it == items_.end())\n        client->OnError();\n}\n\nvoid QMigrateClient::OnConnect()\n{\n    auto& mgr = QMigrationManager::Instance();\n    mgr.OnConnect(this);\n\n    auto it = mgr.items_.find(peerAddr_);\n    assert (it != mgr.items_.end());\n    for (auto& item : it->second)\n    {\n        auto c = item.client.lock();\n        if (!c)\n        {\n            item.state = MigrateState::Done;\n            continue;\n        }\n\n        if (item.state == MigrateState::None)\n        {\n            ProcessItem(item, this);\n        }\n    }\n}\n\nPacketLength QMigrateClient::_HandlePacket(const char* msg, std::size_t len)\n{\n    if (len < 5)\n        return 0;\n\n    const char* crlf = SearchCRLF(msg, len);\n    if (!crlf)\n        return 0;\n\n    ++ readyRsp_;\n\n    auto it = QMigrationManager::Instance().items_.find(peerAddr_);\n    auto& item = it->second.front();\n    if (item.keys.size() + 1 == readyRsp_) // plus select db\n    {\n        readyRsp_ = 0;\n        auto c = item.client.lock();\n\n        if (c)\n            c->SendPacket(\"+OK\\r\\n\", 5);\n\n        if (!item.copy)\n        {\n            std::vector<QString> params {\"del\"};\n            params.insert(params.end(), item.keys.begin(), item.keys.end());\n\n            extern QError del(const std::vector<QString>& , UnboundedBuffer* );\n            del(params, nullptr);\n            Propogate(params);\n        }\n        \n        item.state = MigrateState::Done;\n    }\n\n    return static_cast<PacketLength>(crlf + 2 - msg);\n}\n\n// migrate host port key dst-db timeout COPY REPLACE KEYS key1 key2\nQError migrate(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    try {\n        struct MigrationItem item;\n        item.dst.Init(params[1].data(), std::stoi(params[2]));\n        item.dstDb = std::stoi(params[4]);\n        item.timeout = ::time(nullptr) + (std::stoi(params[5]) + 999) / 1000;\n\n        const int kUnused = -1;\n        int copy = kUnused;\n        int replace = kUnused;\n        bool withKeys = false;\n        for (size_t i = 6; i < params.size(); ++ i)\n        {\n            if (withKeys)\n            {\n                item.keys.push_back(params[i]);\n            }\n            else\n            {\n                // must be COPY REPLACE or KEYS\n                if (strncasecmp(params[i].c_str(), \"copy\", 4))\n                {\n                    if (copy == kUnused)\n                        copy = 1;\n                    else\n                        throw std::runtime_error(\"wrong syntax for migrate\");\n                }\n                else if (strncasecmp(params[i].c_str(), \"replace\", 7))\n                {\n                    if (replace == kUnused)\n                        replace = 1;\n                    else\n                        throw std::runtime_error(\"wrong syntax for migrate\");\n                }\n                else if (strncasecmp(params[i].c_str(), \"keys\", 4))\n                {\n                    withKeys = true;\n                }\n                else\n                {\n                    throw std::runtime_error(\"wrong syntax for migrate\");\n                }\n            }\n        }\n\n        if (!withKeys && !params[3].empty())\n            item.keys.push_back(params[3]);\n\n        if (copy == 1)\n            item.copy = true;\n        if (replace == 1)\n            item.replace = true;\n\n        item.client = std::static_pointer_cast<StreamSocket>(QClient::Current()->shared_from_this());\n        QMigrationManager::Instance().Add(std::move(item));\n\n        return QError_ok;\n    } catch (...) {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n        \n    return QError_ok;\n}\n\n} // end namespace qedis\n\n"
  },
  {
    "path": "QedisCore/QMigration.h",
    "content": "#ifndef BERT_QMIGRATION_H\n#define BERT_QMIGRATION_H\n\n#include <deque>\n#include \"QClient.h\"\n\nnamespace ConnectionTag\n{\nconst int kMigrateClient = 4;\n}\n\nnamespace qedis\n{\n\nenum class MigrateState\n{\n    None,\n    Processing,\n    Timeout,\n    Done,\n};\n\nstruct MigrationItem\n{\n    SocketAddr dst;\n    int dstDb = 0;\n    time_t timeout = 0;\n    bool copy = false;\n    bool replace = false;\n    std::vector<QString> keys;\n    std::weak_ptr<StreamSocket> client;\n\n    MigrateState state = MigrateState::None;\n};\n\nclass QMigrateClient;\n\nclass QMigrationManager\n{\n    friend class QMigrateClient;\npublic:\n    static QMigrationManager& Instance();\n    \n    void Add(const MigrationItem& item);\n    void Add(MigrationItem&& item);\n\n    void InitMigrationTimer();\n    void LoopCheck();\n\n    std::shared_ptr<QMigrateClient> GetConnection(const SocketAddr& dst);\n    void OnConnectMigrateFail(const SocketAddr& dst);\n    void OnConnect(QMigrateClient* );\n\nprivate:\n    QMigrationManager() { }\n\n    std::unordered_map<SocketAddr, std::deque<MigrationItem> > items_;\n    std::unordered_map<SocketAddr, std::weak_ptr<QMigrateClient> > conns_;\n\n    std::unordered_set<SocketAddr> pendingConnect_;\n};\n\nclass QMigrateClient : public StreamSocket\n{\npublic:\n    void OnConnect() override;\n\nprivate:\n    PacketLength _HandlePacket(const char* msg, std::size_t len) override;\n    size_t readyRsp_ = 0;\n};\n\n} // end namespace qedis\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QModule.cc",
    "content": "#include <sstream>\n#include <dlfcn.h>\n#include <assert.h>\n\n#include \"QCommon.h\"\n#include \"QModule.h\"\n\nnamespace qedis\n{\n\nQModule::QModule() : handler_(nullptr)\n{\n}\n\nQModule::~QModule() \n{\n    UnLoad();\n}\n\nconst QString& QModule::Name() const\n{\n    return soname_;\n}\n\nvoid QModule::Load(const char* so, bool lazy) throw(std::runtime_error)\n{\n    assert (!handler_);\n\n    ::dlerror(); // clear errors\n    handler_ = ::dlopen(so, lazy ? RTLD_LAZY : RTLD_NOW);\n    if (!handler_)\n    {\n        // try local path\n        std::string localName(\"./\");\n        localName += so;\n\n        handler_ = ::dlopen(localName.c_str(), lazy ? RTLD_LAZY : RTLD_NOW);\n\n        if (!handler_)\n        {\n            std::ostringstream oss;\n            oss << \"open [\" << so << \"] failed because:\" << ::dlerror();\n\n            throw std::runtime_error(oss.str());\n        }\n    }\n}\n\nvoid QModule::UnLoad()\n{\n    if (handler_)\n    {\n        ::dlclose(handler_);\n        handler_ = nullptr;\n    }\n}\n\nvoid* QModule::Symbol(const char* symbol)\n{\n    if (handler_)\n    {\n        ::dlerror();\n        return ::dlsym(handler_, symbol);\n    }\n\n    return nullptr;\n}\n\nQModuleManager& QModuleManager::Instance()\n{\n    static QModuleManager mgr;\n    return mgr;\n}\n    \n\nQModule* QModuleManager::Load(const char* so, bool lazy) throw(std::logic_error, std::runtime_error)\n{\n    auto module = std::make_shared<QModule>();\n    module->Load(so, lazy);\n    \n    if (!modules_.insert({QString(so), module}).second)\n        throw ModuleExist(so);\n\n    // try call init function\n    using InitFunc = bool (*)();\n    \n    InitFunc initmodule = (InitFunc)(module->Symbol(\"QedisModule_OnLoad\"));\n    if (!initmodule || !initmodule())\n    {\n        module->UnLoad();\n        modules_.erase(so);\n        throw ModuleNoLoad(so);\n    }\n\n    return module.get();\n}\n\nvoid QModuleManager::UnLoad(const char* so)\n{\n    auto it = modules_.find(so);\n    if (it == modules_.end())\n        throw ModuleNotExist(so);\n    \n    QEDIS_DEFER\n    {\n        it->second->UnLoad();\n        modules_.erase(it);\n    };\n    \n    // try call uninit function\n    using UnInitFunc = void (*)();\n    \n    UnInitFunc uninit = (UnInitFunc)(it->second->Symbol(\"QedisModule_OnUnLoad\"));\n    if (uninit)\n    {\n        uninit();\n    }\n    else\n    {\n        throw ModuleNoUnLoad(so);\n    }\n}\n\nQModule* QModuleManager::GetModule(const char* so)\n{\n    auto it = modules_.find(so);\n    if (it == modules_.end())\n        return nullptr;\n\n    return it->second.get();\n}\n\nstd::vector<QString> QModuleManager::NameList() const\n{\n    std::vector<QString>  namelist;\n    for (const auto& kv : modules_)\n    {\n        namelist.push_back(kv.first);\n    }\n\n    return namelist;\n}\n\n// MODULE LOAD /path/to/mymodule.so\n// MODULE LIST\n// MODULE UNLOAD mymodule\nQError module(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // MODULE LOAD /path/to/mymodule.{so,dylib}\n    if (strncasecmp(params[1].c_str(), \"load\", 4) == 0)\n    {\n        if (params.size() != 3)\n        {\n            ReplyError(QError_syntax, reply);\n            return QError_syntax;\n        }\n\n        try\n        {\n            MODULES.Load(params[2].c_str());\n        }\n        catch (const ModuleNoLoad& e)\n        {\n            ReplyError(QError_moduleinit, reply);\n            return QError_moduleinit;\n        }\n        catch (const ModuleExist& e)\n        {\n            ReplyError(QError_modulerepeat, reply);\n            return QError_modulerepeat;\n        }\n        catch (...)\n        {\n            ReplyError(QError_nomodule, reply);\n            return QError_nomodule;\n        }\n    }\n    // MODULE LIST\n    else if (strncasecmp(params[1].c_str(), \"list\", 4) == 0)\n    {\n        auto names = MODULES.NameList();\n        PreFormatMultiBulk(names.size(), reply);\n        for (const auto& name : names)\n        {\n            FormatBulk(name, reply);\n        }\n\n        return QError_ok;\n    }\n    // MODULE UNLOAD mymodule\n    else if (strncasecmp(params[1].c_str(), \"unload\", 6) == 0)\n    {\n        if (params.size() != 3)\n        {\n            ReplyError(QError_syntax, reply);\n            return QError_syntax;\n        }\n\n        try\n        {\n            MODULES.UnLoad(params[2].c_str());\n        }\n        catch (const ModuleNotExist& )\n        {\n            ReplyError(QError_nomodule, reply);\n            return QError_nomodule;\n        }\n        catch (const ModuleNoUnLoad& )\n        {\n            ReplyError(QError_moduleuninit, reply);\n            return QError_moduleuninit;\n        }\n    }\n    else\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n       \n    FormatOK(reply);\n    return QError_ok;\n}\n\n}\n\n"
  },
  {
    "path": "QedisCore/QModule.h",
    "content": "#ifndef BERT_QMODULE_H\n#define BERT_QMODULE_H\n\n#include <stdexcept>\n#include <unordered_map>\n#include <vector>\n#include <memory>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nclass ModuleExist : public std::logic_error\n{\npublic:\n    explicit ModuleExist(const QString& what) : std::logic_error(what)\n    {\n    }\n};\n    \nclass ModuleNotExist : public std::logic_error\n{\npublic:\n    explicit ModuleNotExist(const QString& what) : std::logic_error(what)\n    {\n    }\n};\n    \nclass ModuleNoLoad : public std::logic_error\n{\npublic:\n    explicit ModuleNoLoad(const QString& what) : std::logic_error(what)\n    {\n    }\n};\n    \nclass ModuleNoUnLoad : public std::logic_error\n{\npublic:\n    explicit ModuleNoUnLoad(const QString& what) : std::logic_error(what)\n    {\n    }\n};\n    \n\nclass QModule\n{\npublic:\n    QModule();\n    ~QModule();\n\n    const QString& Name() const;\n\n    void Load(const char* so, bool lazy = false) throw(std::runtime_error);\n    void UnLoad();\n    void* Symbol(const char* symbol);\n\nprivate:\n    QString soname_;\n    void* handler_;\n};\n\nclass QModuleManager\n{\npublic:\n    static QModuleManager& Instance();\n    \n    QModuleManager(const QModuleManager& ) = delete;\n    void operator= (const QModuleManager& ) = delete;\n\n    QModule* Load(const char* so, bool lazy = false) throw(std::logic_error, std::runtime_error);\n    void UnLoad(const char* so);\n\n    QModule* GetModule(const char* so);\n\n    std::vector<QString> NameList() const;\n\nprivate:\n    QModuleManager() {}\n\n    std::unordered_map<QString, std::shared_ptr<QModule> >  modules_;\n};\n\n#define MODULES  ::qedis::QModuleManager::Instance()\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QMulti.cc",
    "content": "#include \"QMulti.h\"\n#include \"QClient.h\"\n#include \"QStore.h\"\n#include \"Log/Logger.h\"\n\nnamespace qedis\n{\n\nQMulti&    QMulti::Instance()\n{\n    static QMulti  mt;\n    return mt;\n}\n    \nvoid  QMulti::Watch(QClient* client, int dbno, const QString& key)\n{\n    if (client->Watch(dbno, key))\n    {\n        Clients& cls = clients_[dbno][key];\n        cls.push_back(std::static_pointer_cast<QClient>(client->shared_from_this()));\n    }\n}\n\n\nbool QMulti::Multi(QClient* client)\n{\n    if (client->IsFlagOn(ClientFlag_multi))\n        return false;\n\n    client->ClearMulti();\n    client->SetFlag(ClientFlag_multi);\n    return true;\n}\n\nbool QMulti::Exec(QClient* client)\n{\n    return client->Exec();\n}\n\nvoid QMulti::Discard(QClient* client)\n{\n    client->ClearMulti();\n    client->ClearWatch();\n}\n\n\nvoid  QMulti::NotifyDirty(int dbno, const QString& key)\n{\n    auto tmpDbIter = clients_.find(dbno);\n    if (tmpDbIter == clients_.end())\n        return;\n    \n    auto& dbWatchedKeys = tmpDbIter->second;\n    auto it = dbWatchedKeys.find(key);\n    if (it == dbWatchedKeys.end())\n        return;\n    \n    Clients& cls = it->second;\n    for (auto itCli(cls.begin()); itCli != cls.end(); )\n    {\n        auto client(itCli->lock());\n        if (!client)\n        {\n            WRN << \"Erase not exist client when notify dirty key[\" << key << \"]\";\n            itCli = cls.erase(itCli);\n        }\n        else\n        {\n            if (client.get() != QClient::Current() && client->NotifyDirty(dbno, key))\n            {\n                \n                WRN << \"Erase dirty client \"\n                    << client->GetName()\n                    << \" when notify dirty key[\"\n                    << key << \"]\";\n                itCli = cls.erase(itCli);\n            }\n            else\n            {\n                ++ itCli;\n            }\n        }\n    }\n        \n    if (cls.empty())\n    {\n        dbWatchedKeys.erase(it);\n    }\n}\n\nvoid  QMulti::NotifyDirtyAll(int dbno)\n{\n    if (dbno == -1)\n    {\n        for (auto& db_set : clients_)\n        {\n            for (auto& key_clients : db_set.second)\n            {\n                std::for_each(key_clients.second.begin(), key_clients.second.end(), [&] (const std::weak_ptr<QClient>& wcli)\n                {\n                     auto scli = wcli.lock();\n                     if (scli) scli->SetFlag(ClientFlag_dirty);\n                } );\n            }\n        }\n    }\n    else\n    {\n        auto it = clients_.find(dbno);\n        if (it != clients_.end())\n        {\n            for (auto& key_clients : it->second)\n            {\n                std::for_each(key_clients.second.begin(), key_clients.second.end(), [&] (const std::weak_ptr<QClient>& wcli)\n                {\n                     auto scli = wcli.lock();\n                     if (scli) scli->SetFlag(ClientFlag_dirty);\n                } );\n            }\n        }\n    }\n}\n\n// multi commands\nQError  watch(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    if (client->IsFlagOn(ClientFlag_multi))\n    {\n        ReplyError(QError_watch, reply);\n        return  QError_watch;\n    }\n    \n    std::for_each(++ params.begin(), params.end(), [client](const QString& s) {\n        QMulti::Instance().Watch(client, QSTORE.GetDB(), s);\n    } );\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError  unwatch(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    client->ClearWatch();\n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError  multi(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    if (QMulti::Instance().Multi(client))\n        FormatOK(reply);\n    else\n        reply->PushData(\"-ERR MULTI calls can not be nested\\r\\n\",\n                 sizeof \"-ERR MULTI calls can not be nested\\r\\n\" - 1);\n\n    return QError_ok;\n}\n\nQError  exec(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    if (!client->IsFlagOn(ClientFlag_multi))\n    {\n        ReplyError(QError_noMulti, reply);\n        return QError_noMulti;\n    }\n    if (!QMulti::Instance().Exec(client))\n    {\n        ReplyError(QError_dirtyExec, reply);\n        return  QError_dirtyExec;\n    }\n    return QError_ok;\n}\n\nQError  discard(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    if (!client->IsFlagOn(ClientFlag_multi))\n    {\n        reply->PushData(\"-ERR DISCARD without MULTI\\r\\n\",\n                 sizeof \"-ERR DISCARD without MULTI\\r\\n\" - 1);\n    }\n    else\n    {\n        QMulti::Instance().Discard(client);\n        FormatOK(reply);\n    }\n\n    return QError_ok;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QMulti.h",
    "content": "#ifndef BERT_QMULTI_H\n#define BERT_QMULTI_H\n\n#include <map>\n#include <vector>\n#include <memory>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nclass QClient;\nclass QMulti\n{\npublic:\n    static QMulti& Instance();\n\n    QMulti(const QMulti& ) = delete;\n    void operator= (const QMulti& ) = delete;\n\n    void  Watch(QClient* client, int dbno, const QString& key);\n    bool  Multi(QClient* client);\n    bool  Exec(QClient* client);\n    void  Discard(QClient* client);\n\n    void  NotifyDirty(int dbno, const QString& key);\n    void  NotifyDirtyAll(int dbno);\nprivate:\n    QMulti() {}\n\n    using Clients = std::vector<std::weak_ptr<QClient> >;\n    using WatchedClients = std::map<int, std::map<QString, Clients> >;\n    \n    WatchedClients  clients_;\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QProtoParser.cc",
    "content": "\n\n\n#include \"QCommon.h\"\n#include \"QProtoParser.h\"\n\n#include <assert.h>\n\n// 1 request -> multi strlist\n// 2 multi -> * number crlf\n// 3 strlist -> str strlist | empty\n// 4 str -> strlen strval\n// 5 strlen -> $ number crlf\n// 6 strval -> string crlf\n\nnamespace qedis\n{\n\nvoid QProtoParser::Reset()\n{\n    multi_ = -1;\n    paramLen_ = -1;\n    numOfParam_ = 0;\n\n    // Optimize: Most redis command has 3 args\n    while (params_.size() > 3)\n        params_.pop_back();\n}\n\nQParseResult QProtoParser::ParseRequest(const char*& ptr, const char* end)\n{\n    if (multi_ == -1)\n    {\n        auto parseRet = _ParseMulti(ptr, end, multi_);\n        if (parseRet == QParseResult::error ||\n            multi_ < -1)\n            return QParseResult::error;\n\n        if (parseRet != QParseResult::ok)\n            return QParseResult::wait;\n    }\n\n    return _ParseStrlist(ptr, end, params_);\n}\n\nQParseResult QProtoParser::_ParseMulti(const char*& ptr, const char* end, int& result)\n{\n    if (end - ptr < 3)\n        return QParseResult::wait;\n\n    if (*ptr != '*')\n        return QParseResult::error;\n\n    ++ ptr;\n\n    return GetIntUntilCRLF(ptr,  end - ptr, result);\n}\n\nQParseResult QProtoParser::_ParseStrlist(const char*& ptr, const char* end, std::vector<QString>& results)\n{\n    while (static_cast<int>(numOfParam_) < multi_)\n    {\n        if (results.size() < numOfParam_ + 1)\n            results.resize(numOfParam_ + 1);\n\n        auto parseRet = _ParseStr(ptr, end, results[numOfParam_]);\n\n        if (parseRet == QParseResult::ok)\n        {\n            ++ numOfParam_;\n        }\n        else\n        {\n            return parseRet;\n        }\n    }\n\n    results.resize(numOfParam_);\n    return QParseResult::ok;\n}\n\nQParseResult QProtoParser::_ParseStr(const char*& ptr, const char* end, QString& result)\n{\n    if (paramLen_ == -1)\n    {\n        auto parseRet = _ParseStrlen(ptr, end, paramLen_);\n        if (parseRet == QParseResult::error ||\n            paramLen_ < -1)\n            return QParseResult::error;\n\n        if (parseRet != QParseResult::ok)\n            return QParseResult::wait;\n    }\n\n    if (paramLen_ == -1)\n    {\n        result.clear(); // or should be \"(nil)\" ?\n        return QParseResult::ok;\n    }\n    else\n    {\n        return _ParseStrval(ptr, end, result);\n    }\n}\n\nQParseResult QProtoParser::_ParseStrval(const char*& ptr, const char* end, QString& result)\n{\n    assert (paramLen_ >= 0);\n\n    if (static_cast<int>(end - ptr) < paramLen_ + 2)\n        return QParseResult::wait;\n\n    auto tail = ptr + paramLen_;\n    if (tail[0] != '\\r' || tail[1] != '\\n')\n        return QParseResult::error;\n\n    result.assign(ptr, tail - ptr);\n    ptr = tail + 2;\n    paramLen_ = -1;\n\n    return QParseResult::ok;\n}\n\nQParseResult QProtoParser::_ParseStrlen(const char*& ptr, const char* end, int& result)\n{\n    if (end - ptr < 3)\n        return QParseResult::wait;\n\n    if (*ptr != '$')\n        return QParseResult::error;\n\n    ++ ptr;\n\n    const auto ret = GetIntUntilCRLF(ptr,  end - ptr, result);\n    if (ret != QParseResult::ok)\n        -- ptr;\n\n    return ret;\n}\n\n}\n\n"
  },
  {
    "path": "QedisCore/QProtoParser.h",
    "content": "#ifndef BERT_QPROTOPARSER_H\n#define BERT_QPROTOPARSER_H\n\n#include <vector>\n#include \"QString.h\"\n\nnamespace qedis\n{\n\nclass QProtoParser\n{\npublic:\n    void Reset();\n    QParseResult ParseRequest(const char*& ptr, const char* end);\n\n    const std::vector<QString>& GetParams() const { return params_; }\n    void SetParams(std::vector<QString> p) { params_ = std::move(p); }\n    \n    bool IsInitialState() const { return multi_ == -1; }\n\nprivate:\n    QParseResult _ParseMulti(const char*& ptr, const char* end, int& result);\n    QParseResult _ParseStrlist(const char*& ptr, const char* end, std::vector<QString>& results);\n    QParseResult _ParseStr(const char*& ptr, const char* end, QString& result);\n    QParseResult _ParseStrval(const char*& ptr, const char* end, QString& result);\n    QParseResult _ParseStrlen(const char*& ptr, const char* end, int& result);\n\n    int multi_ = -1;\n    int paramLen_ = -1;\n\n    size_t numOfParam_ = 0; // for optimize\n    std::vector<QString> params_;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "QedisCore/QPubsub.cc",
    "content": "#include \"QPubsub.h\"\n#include \"QClient.h\"\n#include \"Log/Logger.h\"\n#include \"Timer.h\"\n#include \"QGlobRegex.h\"\n\nnamespace qedis\n{\n\nQPubsub& QPubsub::Instance()\n{\n    static QPubsub ps;\n    return ps;\n}\n    \nsize_t QPubsub::Subscribe(QClient* client, const QString& channel)\n{\n    if (client && client->Subscribe(channel))\n    {\n        auto it(channels_.find(channel));\n        if (it == channels_.end())\n            it = channels_.insert(ChannelClients::value_type(channel, Clients())).first;\n\n        assert (it != channels_.end());\n\n        auto c = std::static_pointer_cast<QClient>(client->shared_from_this());\n        bool succ = it->second.insert(std::move(c)).second;\n        assert (succ);\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstd::size_t QPubsub::UnSubscribe(QClient* client, const QString& channel)\n{\n    if (client && client->UnSubscribe(channel))\n    {\n        auto it(channels_.find(channel));\n        assert (it != channels_.end());\n\n        Clients& clientSet = it->second;\n\n        auto c = std::static_pointer_cast<QClient>(client->shared_from_this());\n        std::size_t n = clientSet.erase(c);\n        assert (n == 1);\n\n        if (clientSet.empty())\n            channels_.erase(it);\n\n        return client->ChannelCount();\n    }\n\n    return 0;\n}\n\nstd::size_t QPubsub::UnSubscribeAll(QClient* client)\n{\n    if (!client)  return 0;\n\n    std::size_t  n = 0;\n    const auto& channels = client->GetChannels();\n    for (const auto& channel : channels)\n    {\n        n += UnSubscribe(client, channel);\n    }\n\n    return n;\n}\n\nsize_t QPubsub::PSubscribe(QClient* client, const QString& channel)\n{\n    if (client && client->PSubscribe(channel))\n    {\n        auto it(patternChannels_.find(channel));\n        if (it == patternChannels_.end())\n            it = patternChannels_.insert({channel, Clients()}).first;\n\n        assert (it != patternChannels_.end());\n\n        auto c = std::static_pointer_cast<QClient>(client->shared_from_this());\n        bool succ = it->second.insert(c).second;\n        assert (succ);\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstd::size_t QPubsub::PUnSubscribe(QClient* client, const QString& channel)\n{\n    if (client && client->PUnSubscribe(channel))\n    {\n        auto it(patternChannels_.find(channel));\n        assert (it != patternChannels_.end());\n\n        Clients& clientSet = it->second;\n\n        auto c = std::static_pointer_cast<QClient>(client->shared_from_this());\n        std::size_t n = clientSet.erase(c);\n        assert (n == 1);\n\n        if (clientSet.empty())\n            patternChannels_.erase(it);\n\n        return client->PatternChannelCount();\n    }\n\n    return 0;\n}\n\nstd::size_t QPubsub::PUnSubscribeAll(QClient* client)\n{\n    if (!client)  return 0;\n\n    std::size_t  n = 0;\n    const auto& channels = client->GetPatternChannels();\n    for (const auto& channel : channels)\n    {\n        n += PUnSubscribe(client, channel);\n    }\n\n    return n;\n}\n    \nsize_t QPubsub::_Publish(QPubsub::Clients& clients, const std::vector<QString>& args)\n{\n    size_t n = 0;\n    for (auto itCli(clients.begin()); itCli != clients.end(); )\n    {\n        auto cli = itCli->lock();\n        if (!cli)\n        {\n            itCli = clients.erase(itCli);\n        }\n        else\n        {\n            SocketAddr peer;\n            Socket::GetPeerAddr(cli->GetSocket(), peer);\n            INF << \"Publish msg:\" << args.back() << \" to \" << peer.ToString();\n            \n            UnboundedBuffer reply;\n            PreFormatMultiBulk(args.size(), &reply);\n            for (const auto& arg : args)\n            {\n                FormatBulk(arg, &reply);\n            }\n            cli->SendPacket(reply);\n            ++ itCli;\n            ++ n;\n        }\n    }\n    \n    return n;\n}\n\nstd::size_t QPubsub::PublishMsg(const QString& channel, const QString& msg)\n{\n    std::size_t n = 0;\n\n    auto it(channels_.find(channel));\n    if (it != channels_.end())\n    {\n        n += _Publish(it->second, {\"message\", channel, msg});\n    }\n\n    for (auto& pattern : patternChannels_)\n    {\n        if (glob_match(pattern.first, channel))\n        {\n            n += _Publish(pattern.second, {\"pmessage\", pattern.first, channel, msg});\n            INF << channel << \" match \" << pattern.first;\n        }\n    }\n\n    return  n;\n}\n\nvoid QPubsub::RecycleClients(QString& startChannel, QString& startPattern)\n{\n    _RecycleClients(channels_, startChannel);\n    _RecycleClients(patternChannels_, startPattern);\n}\n\nvoid QPubsub::_RecycleClients(ChannelClients& channels, QString& start)\n{\n    auto it(start.empty() ? channels.begin() : channels.find(start));\n    if (it == channels.end())\n        it = channels.begin();\n\n    const size_t kEraseMax = 10;\n    size_t n = 0;\n    while (it != channels.end() && n < kEraseMax)\n    {\n        Clients& cls = it->second;\n        for (auto itCli(cls.begin()); itCli != cls.end(); )\n        {\n            if (itCli->expired())\n            {\n                INF << \"_RecycleClient on channel \" << it->first;\n                itCli = cls.erase(itCli);\n                ++ n;\n            }\n            else\n            {\n                ++ itCli;\n            }\n        }\n        \n        if (cls.empty())\n        {\n            INF << \"erase channel \" << it->first;\n            it = channels.erase(it);\n        }\n        else\n        {\n            ++ it;\n        }\n    }\n\n    if (it != channels.end())\n        start = it->first;\n    else\n        start.clear();\n}\n\nvoid QPubsub::InitPubsubTimer()\n{\n    auto timer = TimerManager::Instance().CreateTimer();\n    timer->Init(200);\n    timer->SetCallback([&] (std::string& channel, std::string& pattern) {\n            QPubsub::Instance().RecycleClients(channel, pattern);\n        },\n        std::ref(startChannel_),\n        std::ref(startPattern_));\n    \n    TimerManager::Instance().AddTimer(timer);\n}\n\nvoid QPubsub::PubsubChannels(std::vector<QString>& res, const char* pattern) const\n{\n    res.clear();\n\n    for (const auto& elem : channels_)\n    {\n        if (!pattern || glob_match(pattern, elem.first))\n        {\n            res.push_back(elem.first);\n        }\n    }\n}\n\n\nsize_t  QPubsub::PubsubNumsub(const QString& channel) const\n{\n    auto it = channels_.find(channel);\n    \n    if (it != channels_.end())\n        return it->second.size();\n    \n    return 0;\n}\n\nsize_t QPubsub::PubsubNumpat() const\n{\n    std::size_t n = 0;\n    \n    for (const auto& elem : patternChannels_)\n    {\n        n += elem.second.size();\n    }\n    \n    return n;\n}\n\n// pubsub commands\nQError  subscribe(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    for (size_t i = 1; i < params.size(); ++ i)\n    {\n        const auto& pa = params[i];\n        size_t n = QPubsub::Instance().Subscribe(client, pa);\n        if (n == 1)\n        {\n            PreFormatMultiBulk(3, reply);\n            FormatBulk(\"subscribe\", 9, reply);\n            FormatBulk(pa, reply);\n            FormatInt(client->ChannelCount(), reply);\n\n            SocketAddr peer;\n            Socket::GetPeerAddr(client->GetSocket(), peer);\n            INF << \"subscribe \" << pa << \" by \" << peer.ToString();\n        }\n    }\n\n    return QError_ok;\n}\n\nQError  psubscribe(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n    for (size_t i = 1; i < params.size(); ++ i)\n    {\n        const auto& pa = params[i];\n        size_t n = QPubsub::Instance().PSubscribe(client, pa);\n        if (n == 1)\n        {\n            PreFormatMultiBulk(3, reply);\n            FormatBulk(\"psubscribe\", 9, reply);\n            FormatBulk(pa, reply);\n            FormatInt(client->PatternChannelCount(), reply);\n\n            SocketAddr peer;\n            Socket::GetPeerAddr(client->GetSocket(), peer);\n            INF << \"psubscribe \" << pa << \" by \" << peer.ToString();\n        }\n    }\n\n    return QError_ok;\n}\n\n\nQError  unsubscribe(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n\n    if (params.size() == 1)\n    {\n        const auto& channels = client->GetChannels();\n        for (const auto& channel : channels)\n        {\n            FormatBulk(channel, reply);\n        }\n        \n        QPubsub::Instance().UnSubscribeAll(client);\n    }\n    else\n    {\n        std::set<QString> channels;\n\n        for (size_t i = 1; i < params.size(); ++ i)\n        {\n            size_t n = QPubsub::Instance().UnSubscribe(client, params[i]);\n            if (n == 1)\n            {\n                channels.insert(params[i]);\n\n                SocketAddr peer;\n                Socket::GetPeerAddr(client->GetSocket(), peer);\n                INF << \"unsubscribe \" << params[i] << \" by \" << peer.ToString();\n            }\n        }\n\n        PreFormatMultiBulk(channels.size(), reply);\n        for (const auto& channel : channels)\n        {\n            FormatBulk(channel, reply);\n        }\n    }\n\n    return  QError_ok;\n}\n\nQError  punsubscribe(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* client = QClient::Current();\n\n    if (params.size() == 1)\n    {\n        const auto& channels = client->GetPatternChannels();\n        for (const auto& channel : channels)\n        {\n            FormatBulk(channel, reply);\n        }\n        \n        QPubsub::Instance().PUnSubscribeAll(client);\n    }\n    else\n    {\n        std::set<QString> channels;\n\n        for (size_t i = 1; i < params.size(); ++ i)\n        {\n            size_t n = QPubsub::Instance().PUnSubscribe(client, params[i]);\n            if (n == 1)\n            {\n                channels.insert(params[i]);\n\n                SocketAddr peer;\n                Socket::GetPeerAddr(client->GetSocket(), peer);\n                INF << \"punsubscribe \" << params[i] << \" by \" << peer.ToString();\n            }\n        }\n\n        PreFormatMultiBulk(channels.size(), reply);\n        for (const auto& channel : channels)\n        {\n            FormatBulk(channel, reply);\n        }\n    }\n\n    return  QError_ok;\n}\n\nQError  publish(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    size_t n = QPubsub::Instance().PublishMsg(params[1], params[2]);\n    FormatInt(n, reply);\n\n    return QError_ok;\n}\n\n// neixing command\nQError  pubsub(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params[1] == \"channels\")\n    {\n        if (params.size() > 3)\n        {\n            ReplyError(QError_param, reply);\n            return QError_param;\n        }\n\n        std::vector<QString> res;\n        QPubsub::Instance().PubsubChannels(res, params.size() == 3 ? params[2].c_str() : 0);\n        PreFormatMultiBulk(res.size(), reply);\n        for (const auto& channel : res)\n        {\n            FormatBulk(channel, reply);\n        }\n    }\n    else if (params[1] == \"numsub\")\n    {\n        PreFormatMultiBulk(2 * (params.size() - 2), reply);\n        for (size_t i = 2; i < params.size(); ++ i)\n        {\n            size_t n = QPubsub::Instance().PubsubNumsub(params[i]);\n            FormatBulk(params[i], reply);\n            FormatInt(n, reply);\n        }\n    }\n    else if (params[1] == \"numpat\")\n    {\n        if (params.size() != 2)\n        {\n            ReplyError(QError_param, reply);\n            return QError_param;\n        }\n        \n        FormatInt(QPubsub::Instance().PubsubNumpat(), reply);\n    }\n    else\n    {\n        ERR << \"Unknown pubsub subcmd \" << params[1].c_str();\n    }\n\n    return QError_ok;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QPubsub.h",
    "content": "#ifndef BERT_QPUBSUB_H\n#define BERT_QPUBSUB_H\n\n#include <map>\n#include <set>\n#include <vector>\n#include \"QString.h\"\n#include <memory>\n\nnamespace qedis\n{\n\nclass QClient;\nclass QPubsub\n{\npublic:\n    static QPubsub& Instance();\n    \n    QPubsub(const QPubsub& ) = delete;\n    void operator= (const QPubsub& ) = delete;\n\n    std::size_t Subscribe(QClient* client, const QString& channel);\n    std::size_t UnSubscribe(QClient* client, const QString& channel);\n    std::size_t UnSubscribeAll(QClient* client);\n    std::size_t PublishMsg(const QString& channel, const QString& msg);\n\n    std::size_t PSubscribe(QClient* client, const QString& pchannel);\n    std::size_t PUnSubscribeAll(QClient* client);\n    std::size_t PUnSubscribe(QClient* client, const QString& pchannel);\n    \n    // introspect\n    void  PubsubChannels(std::vector<QString>& res, const char* pattern = 0) const;\n    std::size_t PubsubNumsub(const QString& channel) const;\n    std::size_t PubsubNumpat() const;\n\n    void InitPubsubTimer();\n    void RecycleClients(QString& startChannel, QString& startPattern);\n\nprivate:\n    QPubsub() {}\n\n    using Clients = std::set<std::weak_ptr<QClient>, std::owner_less<std::weak_ptr<QClient> > >;\n    using ChannelClients = std::map<QString, Clients>;\n\n    ChannelClients   channels_;\n    ChannelClients   patternChannels_;\n    \n    QString  startChannel_;\n    QString  startPattern_;\n    static void _RecycleClients(ChannelClients& channels, QString& start);\n    \n    size_t _Publish(QPubsub::Clients& clients, const std::vector<QString>& args);\n};\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QReplication.cc",
    "content": "\n#include <unistd.h>\n#include <iostream> // the child process use stdout for log\n#include <sstream>\n\n#include \"Log/Logger.h\"\n#include \"QClient.h\"\n#include \"QConfig.h\"\n#include \"QCommon.h\"\n#include \"QDB.h\"\n#include \"QReplication.h\"\n\n#include \"QAOF.h\"\n#include \"Server.h\"\n\n\nnamespace qedis\n{\n\nQReplication& QReplication::Instance()\n{\n    static QReplication  rep;\n    return rep;\n}\n\nQReplication::QReplication()\n{\n}\n\nbool QReplication::IsBgsaving() const\n{\n    return  bgsaving_;\n}\n\nvoid QReplication::AddSlave(qedis::QClient* cli)\n{\n    slaves_.push_back(std::static_pointer_cast<QClient>(cli->shared_from_this()));\n}\n\nbool QReplication::HasAnyWaitingBgsave() const\n{\n    for (const auto& c : slaves_)\n    {\n        auto cli = c.lock();\n        if (cli && cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_start)\n        {\n            return true;\n        }\n    }\n    \n    return false;\n}\n\nvoid QReplication::OnRdbSaveDone()\n{\n    bgsaving_ = false;\n    \n    InputMemoryFile  rdb;\n    \n    // send rdb to slaves that wait rdb end, set state\n    for (auto& wptr : slaves_)\n    {\n        auto cli = wptr.lock();\n        if (!cli)\n            continue;\n        \n        if (cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_end)\n        {\n            cli->GetSlaveInfo()->state = QSlaveState_online;\n            \n            if (!rdb.IsOpen() && !rdb.Open(g_config.rdbfullname.c_str()))\n            {\n                ERR << \"can not open rdb when replication\\n\";\n                return;  // fatal error;\n            }\n            \n            std::size_t size = std::size_t(-1);\n            const char* data = rdb.Read(size);\n            \n            // $file_len + filedata\n            char tmp[32];\n            int n =  snprintf(tmp, sizeof tmp - 1, \"$%ld\\r\\n\", (long)size);\n            \n            cli->SendPacket(tmp, n);\n            cli->SendPacket(data, size);\n            cli->SendPacket(buffer_);\n            \n            INF << \"Send to slave rdb \" << size << \", buffer \" << buffer_.ReadableSize();\n        }\n    }\n    \n    buffer_.Clear();\n}\n\n\nvoid QReplication::TryBgsave()\n{\n    if (IsBgsaving())\n        return;\n    \n    if (!HasAnyWaitingBgsave())\n        return;\n    \n    int ret = fork();\n    if (ret == 0)\n    {\n        {\n            QDBSaver  qdb;\n            qdb.Save(g_config.rdbfullname.c_str());\n            std::cerr << \"QReplication save rdb done, exiting child\\n\";\n        }\n        _exit(0);\n    }\n    else if (ret == -1)\n    {\n        ERR << \"QReplication save rdb FATAL ERROR\";\n        _OnStartBgsave(false);\n    }\n    else\n    {\n        INF << \"QReplication save rdb START\";\n        g_qdbPid = ret;\n        _OnStartBgsave(true);\n    }\n}\n\n\nvoid QReplication::_OnStartBgsave(bool succ)\n{\n    buffer_.Clear();\n    bgsaving_ = succ;\n    \n    for (auto& c : slaves_)\n    {\n        auto cli = c.lock();\n        \n        if (!cli)\n            continue;\n    \n        if (cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_start)\n        {\n            if (succ)\n            {\n                INF << \"_OnStartBgsave set cli wait bgsave end \" << cli->GetName();\n                cli->GetSlaveInfo()->state = QSlaveState_wait_bgsave_end;\n            }\n            else\n            {\n                cli->OnError(); // release slave\n            }\n        }\n    }\n}\n\nvoid QReplication::SendToSlaves(const std::vector<QString>& params)\n{\n    if (IsBgsaving())\n    {\n        // 在执行rdb期间，缓存变化\n        SaveCommand(params, buffer_);\n        return;\n    }\n    \n    UnboundedBuffer   ub;\n    \n    for (const auto& wptr : slaves_)\n    {\n        auto cli = wptr.lock();\n        if (!cli || cli->GetSlaveInfo()->state != QSlaveState_online)\n            continue;\n        \n        if (ub.IsEmpty())\n            SaveCommand(params, ub);\n        \n        cli->SendPacket(ub);\n    }\n}\n\nvoid QReplication::Cron()\n{\n    static unsigned pingCron = 0;\n    \n    if (pingCron ++ % 50 == 0)\n    {\n        for (auto it = slaves_.begin(); it != slaves_.end(); )\n        {\n            auto cli = it->lock();\n            if (!cli)\n            {\n                it = slaves_.erase(it);\n            }\n            else\n            {\n                ++ it;\n\n                if (cli->GetSlaveInfo()->state == QSlaveState_online)\n                    cli->SendPacket(\"PING\\r\\n\", 6);\n            }\n        }\n    }\n    \n    if (!masterInfo_.addr.Empty())\n    {\n        switch (masterInfo_.state)\n        {\n            case QReplState_none:\n            {\n                SocketAddr localSvr(g_config.ip.c_str() , g_config.port);\n                if (masterInfo_.addr.GetAddr() == localSvr)\n                {\n                    ERR << \"Fix config, master addr is self addr!\";\n                    assert(!!!\"wrong config for master addr\");\n                }\n                    \n                if (auto master = master_.lock())\n                {\n                    WRN << \"Disconnect from previous master \" << master->GetPeerAddr().GetIP();\n                    master->OnError();\n                }\n                \n                INF << \"Try connect to master \"\n                    << masterInfo_.addr.GetIP()\n                    << \":\" << masterInfo_.addr.GetPort();\n\n                Server::Instance()->TCPConnect(masterInfo_.addr, [&]() {\n                    WRN << \"OnCallback: Connect master failed\";\n\n                    QREPL.SetMasterState(QReplState_none);\n                    if (!masterInfo_.downSince)\n                        masterInfo_.downSince = ::time(nullptr);\n                },\n                ConnectionTag::kQedisClient);\n\n                masterInfo_.state = QReplState_connecting;\n            }\n                break;\n\n            case QReplState_connected:\n                if (!g_config.masterauth.empty())\n                {\n                    if (auto master = master_.lock())\n                    {\n                        UnboundedBuffer req;\n                        req.PushData(\"auth \", 5);\n                        req.PushData(g_config.masterauth.data(), g_config.masterauth.size());\n                        req.PushData(\"\\r\\n\", 2);\n                        master->SendPacket(req);\n                        INF << \"send auth with password \" << g_config.masterauth;\n                        \n                        masterInfo_.state = QReplState_wait_auth;\n                        break;\n                    }\n                }\n                // fall through to next case.\n                \n            case QReplState_wait_auth:\n            {\n                auto master = master_.lock();\n                if (!master)\n                {\n                    masterInfo_.state = QReplState_none;\n                    masterInfo_.downSince = ::time(nullptr);\n                    WRN << \"Master is down from wait_auth to none\";\n                }\n                else if (master->GetAuth())\n                {\n                    // send replconf\n                    char req[128];\n                    auto len = snprintf(req, sizeof req - 1,\n                             \"replconf listening-port %hu\\r\\n\", g_config.port);\n                    master->SendPacket(req, len);\n                    masterInfo_.state = QReplState_wait_replconf;\n\n                    INF << \"Send replconf listening-port \" << g_config.port;\n                }\n                else\n                {\n                    WRN << \"Haven't auth to master yet, or check masterauth password\";\n                }\n            }\n                break;\n\n            case QReplState_wait_replconf:\n            {\n                auto master = master_.lock();\n                if (!master)\n                {\n                    masterInfo_.state = QReplState_none;\n                    masterInfo_.downSince = ::time(nullptr);\n                    WRN << \"Master is down from wait_replconf to none\";\n                }\n                else\n                {\n                    // request sync rdb file\n                    master->SendPacket(\"SYNC\\r\\n\", 6);\n                    INF << \"Request SYNC\";\n                    \n                    rdb_.Open(slaveRdbFile, false);\n                    masterInfo_.rdbRecved = 0;\n                    masterInfo_.rdbSize   = std::size_t(-1);\n                    masterInfo_.state = QReplState_wait_rdb;;\n                }\n            }\n                break;\n\n            case QReplState_wait_rdb:\n                break;\n\n            case QReplState_online:\n                if (auto master = master_.lock())\n                {\n                }\n                else\n                {\n                    masterInfo_.state = QReplState_none;\n                    masterInfo_.downSince = ::time(nullptr);\n                    WRN << \"Master is down\";\n                }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    else\n    {\n        if (masterInfo_.state != QReplState_none)\n        {\n            auto master = master_.lock();\n            if (master)\n            {\n                SocketAddr  peer;\n                Socket::GetPeerAddr(master->GetSocket(), peer);\n                INF << master->GetName() << \" disconnect with Master \" << peer.GetIP();\n                \n                master->SetOnDisconnect();\n                master->OnError();\n            }\n            \n            masterInfo_.state = QReplState_none;\n            masterInfo_.downSince = ::time(nullptr);\n            WRN << \"Master is down from connected to none\";\n        }\n    }\n}\n\n\nvoid QReplication::SaveTmpRdb(const char* data, std::size_t& len)\n{\n    if (masterInfo_.rdbRecved + len > masterInfo_.rdbSize)\n        len = masterInfo_.rdbSize - masterInfo_.rdbRecved;\n\n    rdb_.Write(data, len);\n    masterInfo_.rdbRecved += len;\n    \n    if (masterInfo_.rdbRecved == masterInfo_.rdbSize)\n    {\n        INF << \"Rdb recv complete, bytes \" << masterInfo_.rdbSize;\n        \n        QSTORE.ResetDb();\n        \n        QDBLoader  loader;\n        loader.Load(slaveRdbFile);\n        masterInfo_.state = QReplState_online;\n        masterInfo_.downSince = 0;\n    }\n}\n    \nvoid QReplication::SetMaster(const std::shared_ptr<QClient>&  cli)\n{\n    master_ = cli;\n}\n\nvoid QReplication::SetMasterState(QReplState s)\n{\n    masterInfo_.state = s;\n}\n\nQReplState QReplication::GetMasterState() const\n{\n    return masterInfo_.state;\n}\n\nSocketAddr QReplication::GetMasterAddr() const\n{\n    return masterInfo_.addr;\n}\n\nvoid QReplication::SetMasterAddr(const char* ip, unsigned short port)\n{\n    if (ip)\n        masterInfo_.addr.Init(ip, port);\n    else\n        masterInfo_.addr.Clear();\n}\n    \nvoid QReplication::SetRdbSize(std::size_t s)\n{\n    masterInfo_.rdbSize = s;\n}\n\nstd::size_t QReplication::GetRdbSize() const\n{\n    return masterInfo_.rdbSize;\n}\n\n    \nQError replconf(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 == 0)\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n    \n    for (size_t i = 1; i < params.size(); i += 2)\n    {\n        if (strncasecmp(params[i].c_str(), \"listening-port\", 14) == 0)\n        {\n            long port;\n            if (!TryStr2Long(params[i + 1].c_str(), params[i + 1].size(), port))\n            {\n                ReplyError(QError_param, reply);\n                return QError_param;\n            }\n        \n            auto info = QClient::Current()->GetSlaveInfo();\n            if (!info)\n            {\n                QClient::Current()->SetSlaveInfo();\n                info = QClient::Current()->GetSlaveInfo();\n                QREPL.AddSlave(QClient::Current());\n            }\n            info->listenPort = static_cast<unsigned short>(port);\n        }\n        else\n        {\n            if (reply)\n            {\n                reply->PushData(\"-ERR:Unrecognized REPLCONF option:\",\n                                sizeof \"-ERR:Unrecognized REPLCONF option:\" - 1);\n                reply->PushData(params[i].data(), params[i].size());\n            }\n            \n            return QError_syntax;\n        }\n    }\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n    \n    \nvoid QReplication::OnInfoCommand(UnboundedBuffer& res)\n{\n    const char* slaveState[] = { \"none\",\n        \"wait_bgsave\",\n        \"wait_bgsave\",\n        //\"send_bulk\", // qedis does not have send bulk state\n        \"online\",\n        \n    };\n    \n    std::ostringstream oss;\n    int index = 0;\n    for (const auto& c : slaves_)\n    {\n        auto cli = c.lock();\n        if (cli)\n        {\n            oss << \"slave\" << index << \":\";\n            index ++;\n            \n            char tmpIp[32] = {};\n            cli->GetPeerAddr().GetIP(tmpIp,\n                                     (socklen_t)(sizeof tmpIp));\n            oss << tmpIp;\n            \n            auto slaveInfo = cli->GetSlaveInfo();\n            auto state = slaveInfo ? slaveInfo->state : 0;\n            oss << \",\"\n                << (slaveInfo ? slaveInfo->listenPort : 0)\n                << \",\"\n                << slaveState[state]\n                << \"\\r\\n\";\n        }\n    }\n    \n    std::string slaveInfo(oss.str());\n\n    char buf[1024] = {};\n    bool isMaster = GetMasterAddr().Empty();\n    int n = snprintf(buf, sizeof buf - 1,\n                 \"# Replication\\r\\n\"\n                 \"role:%s\\r\\n\"\n                 \"connected_slaves:%d\\r\\n%s\"\n                 , isMaster ? \"master\" : \"slave\"\n                 , index\n                 , slaveInfo.c_str());\n\n    std::ostringstream masterInfo;\n    if (!isMaster)\n    {\n        char tmpIp[32] = {};\n        masterInfo_.addr.GetIP(tmpIp, (socklen_t)(sizeof tmpIp));\n\n        masterInfo << \"master_host:\"\n                   << tmpIp \n                   << \"\\r\\nmaster_port:\" \n                   << masterInfo_.addr.GetPort()\n                   << \"\\r\\nmaster_link_status:\";\n\n        auto master = master_.lock();\n        masterInfo << (master ? \"up\\r\\n\" : \"down\\r\\n\");\n        if (!master)\n        {\n            if (!masterInfo_.downSince)\n                assert (0);\n\n            masterInfo << \"master_link_down_since_seconds:\"\n                       << (::time(nullptr) - masterInfo_.downSince) << \"\\r\\n\";\n        }\n    }\n    \n    if (!res.IsEmpty())\n        res.PushData(\"\\r\\n\", 2);\n\n    res.PushData(buf, n);\n\n    {\n        std::string info(masterInfo.str());\n        res.PushData(info.c_str(), info.size());\n    }\n}\n    \nQError  slaveof(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (strncasecmp(params[1].data(), \"no\", 2) == 0 &&\n        strncasecmp(params[2].data(), \"one\", 3) == 0)\n    {\n        QREPL.SetMasterAddr(nullptr, 0);\n    }\n    else\n    {\n        unsigned short port = static_cast<unsigned short>(std::stoi(params[2]));\n        SocketAddr reqMaster(params[1].c_str(), port);\n\n        if (port > 0 && QREPL.GetMasterAddr() != reqMaster)\n        {\n            QREPL.SetMasterAddr(params[1].c_str(), port);\n            QREPL.SetMasterState(QReplState_none);\n        }\n    }\n        \n    FormatOK(reply);\n    return QError_ok;\n}\n    \nQError  sync(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient* cli = QClient::Current();\n    auto slave = cli->GetSlaveInfo();\n    if (!slave)\n    {\n        cli->SetSlaveInfo();\n        slave = cli->GetSlaveInfo();\n        QREPL.AddSlave(cli);\n    }\n    \n    if (slave->state == QSlaveState_wait_bgsave_end ||\n        slave->state == QSlaveState_online)\n    {\n        WRN << cli->GetName() << \" state is \"\n            << slave->state << \", ignore this sync request\";\n        \n        return QError_ok;\n    }\n        \n    slave->state = QSlaveState_wait_bgsave_start;\n    QREPL.TryBgsave();\n        \n    return QError_ok;\n}\n    \n\n}\n\n"
  },
  {
    "path": "QedisCore/QReplication.h",
    "content": "#ifndef BERT_QREPLICATION_H\n#define BERT_QREPLICATION_H\n\n#include <list>\n#include <memory>\n#include \"UnboundedBuffer.h\"\n#include \"Socket.h\"\n#include \"Log/MemoryFile.h\"\n\nnamespace qedis\n{\n\n// master side\nenum QSlaveState\n{\n    QSlaveState_none,\n    QSlaveState_wait_bgsave_start, // 有非sync的bgsave进行 要等待\n    QSlaveState_wait_bgsave_end,   // sync bgsave正在进行\n    //QSlaveState_send_rdb, // 这个slave在接受rdb文件\n    QSlaveState_online,\n};\n\nstruct QSlaveInfo\n{\n    QSlaveState  state;\n    unsigned short listenPort; // slave listening port\n    \n    QSlaveInfo() : state(QSlaveState_none), listenPort(0)\n    {\n    }\n};\n\n// slave side\nenum QReplState\n{\n    QReplState_none,\n    QReplState_connecting,\n    QReplState_connected,\n    QReplState_wait_auth, // wait auth to be confirmed\n    QReplState_wait_replconf, // wait replconf to be confirmed\n    QReplState_wait_rdb, // wait to recv rdb file\n    QReplState_online,\n};\n\nstruct QMasterInfo\n{\n    SocketAddr  addr;\n    QReplState  state;\n    time_t downSince;\n    \n    // For recv rdb\n    std::size_t rdbSize;\n    std::size_t rdbRecved;\n    \n    QMasterInfo()\n    {\n        state   = QReplState_none;\n        downSince = 0;\n        rdbSize = std::size_t(-1);\n        rdbRecved = 0;\n    }\n};\n\n//tmp filename\nconst char*  const slaveRdbFile = \"slave.rdb\";\n\nclass QClient;\n\nclass QReplication\n{\npublic:\n    static QReplication& Instance();\n    \n    QReplication(const QReplication& ) = delete;\n    void operator= (const QReplication& ) = delete;\n    \n    void Cron();\n    \n    // master side\n    bool IsBgsaving() const;\n    bool HasAnyWaitingBgsave() const;\n    void AddSlave(QClient* cli);\n    void TryBgsave();\n    bool StartBgsave();\n    void OnStartBgsave();\n    void OnRdbSaveDone();\n    void SendToSlaves(const std::vector<QString>& params);\n    \n    // slave side\n    void SaveTmpRdb(const char* data, std::size_t& len);\n    void SetMaster(const std::shared_ptr<QClient>&  cli);\n    void SetMasterState(QReplState s);\n    void SetMasterAddr(const char* ip, unsigned short port);\n    void SetRdbSize(std::size_t s);\n    QReplState GetMasterState() const;\n    SocketAddr GetMasterAddr() const;\n    std::size_t GetRdbSize() const;\n    \n    // info command\n    void OnInfoCommand(UnboundedBuffer& res);\n\nprivate:\n    QReplication();\n    void _OnStartBgsave(bool succ);\n    \n    // master side\n    bool bgsaving_;\n    UnboundedBuffer buffer_;\n    std::list<std::weak_ptr<QClient> > slaves_;\n\n    //slave side\n    QMasterInfo masterInfo_;\n    std::weak_ptr<QClient> master_;\n    OutputMemoryFile rdb_;\n};\n\n}\n\n#define QREPL  qedis::QReplication::Instance()\n\n#endif\n"
  },
  {
    "path": "QedisCore/QServerCommand.cc",
    "content": "#include <sys/utsname.h>\n#include <cassert>\n#include <unistd.h>\n\n#include \"QStore.h\"\n#include \"QClient.h\"\n#include \"Log/Logger.h\"\n#include \"Server.h\"\n#include \"QDB.h\"\n#include \"QAOF.h\"\n#include \"QConfig.h\"\n#include \"QSlowLog.h\"\n#include \"QGlobRegex.h\"\n#include \"Delegate.h\"\n\n\nnamespace qedis\n{\n\nQError select(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    int newDb = atoi(params[1].c_str());\n    \n    auto client = QClient::Current();\n    \n    if (client)\n    {\n        if (client->SelectDB(newDb))\n            FormatOK(reply);\n        else\n            ReplyError(QError_invalidDB, reply);\n    }\n    else\n    {\n        QSTORE.SelectDB(newDb);\n    }\n\n    return QError_ok;\n}\n\n\nQError dbsize(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    FormatInt(static_cast<long>(QSTORE.DBSize()), reply);\n    return QError_ok;\n}\n\nQError flushdb(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QSTORE.dirty_ += QSTORE.DBSize();\n    QSTORE.ClearCurrentDB();\n    Propogate(QSTORE.GetDB(), params);\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError flushall(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    int currentDb = QSTORE.GetDB();\n    \n    QEDIS_DEFER {\n        QSTORE.SelectDB(currentDb);\n        Propogate(-1, params);\n        QSTORE.ResetDb();\n    };\n    \n    for (int dbno = 0; true; ++ dbno)\n    {\n        if (QSTORE.SelectDB(dbno) == -1)\n            break;\n  \n        QSTORE.dirty_ += QSTORE.DBSize();\n    }\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError bgsave(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (g_qdbPid != -1 || g_rewritePid != -1)\n    {\n        FormatBulk(\"-ERR Background save or aof already in progress\",\n                   sizeof \"-ERR Background save or aof already in progress\" - 1,\n                   reply);\n\n        return QError_ok;\n    }\n   \n    int ret = fork();\n    if (ret == 0)\n    {\n        {\n        QDBSaver  qdb;\n        qdb.Save(g_config.rdbfullname.c_str());\n        }\n        _exit(0);\n    }\n    else if (ret == -1)\n    {\n        FormatSingle(\"Background saving FAILED\", 24, reply);\n    }\n    else\n    {\n        g_qdbPid = ret;\n        FormatSingle(\"Background saving started\", 25, reply);\n    }\n\n    return QError_ok;\n}\n\nQError save(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (g_qdbPid != -1 || g_rewritePid != -1)\n    {\n        FormatBulk(\"-ERR Background save or aof already in progress\",\n                   sizeof \"-ERR Background save or aof already in progress\" - 1,\n                   reply);\n        \n        return QError_ok;\n    }\n    \n    QDBSaver qdb;\n    qdb.Save(g_config.rdbfullname.c_str());\n    g_lastQDBSave = time(NULL);\n\n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError lastsave(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    FormatInt(g_lastQDBSave, reply);\n    return QError_ok;\n}\n\nQError client(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // getname   setname    kill  list\n    QError err = QError_ok;\n    \n    if (params[1].size() == 7 && strncasecmp(params[1].c_str(), \"getname\", 7) == 0)\n    {\n        if (params.size() != 2)\n            ReplyError(err = QError_param, reply);\n        else\n            FormatBulk(QClient::Current()->GetName(),\n                       reply);\n    }\n    else if (params[1].size() == 7 && strncasecmp(params[1].c_str(), \"setname\", 7) == 0)\n    {\n        if (params.size() != 3)\n        {\n            ReplyError(err = QError_param, reply);\n        }\n        else\n        {\n            QClient::Current()->SetName(params[2]);\n            FormatOK(reply);\n        }\n    }\n    else if (params[1].size() == 4 && strncasecmp(params[1].c_str(), \"kill\", 4) == 0)\n    {\n        // only kill current client\n        //QClient::Current()->OnError();\n        FormatOK(reply);\n    }\n    else if (params[1].size() == 4 && strncasecmp(params[1].c_str(), \"list\", 4) == 0)\n    {\n        FormatOK(reply);\n    }\n    else\n    {\n        ReplyError(err = QError_param, reply);\n    }\n    \n    return err;\n}\n\nstatic int Suicide()\n{\n    int* ptr = nullptr;\n    *ptr = 0;\n    \n    return *ptr;\n}\n\nQError debug(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QError err = QError_ok;\n    \n    if (strncasecmp(params[1].c_str(), \"segfault\", 8) == 0 && params.size() == 2)\n    {\n        Suicide();\n        assert (false);\n    }\n    else if (strncasecmp(params[1].c_str(), \"object\", 6) == 0 && params.size() == 3)\n    {\n        QObject* obj = nullptr;\n        err = QSTORE.GetValue(params[2], obj, false);\n        \n        if (err != QError_ok)\n        {\n            ReplyError(err, reply);\n        }\n        else\n        {\n            // ref count,  encoding, idle time\n            char buf[512];\n            int  len = snprintf(buf, sizeof buf, \"ref count:%ld, encoding:%s, idletime:%u\",\n                                1L, // TODO ?\n                                EncodingStringInfo(obj->encoding),\n                                EstimateIdleTime(obj->lru));\n            FormatBulk(buf, len, reply);\n        }\n    }\n    else\n    {\n        ReplyError(err = QError_param, reply);\n    }\n    \n    return err;\n}\n\n\nQError shutdown(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() == 2 && strncasecmp(params[1].c_str(), \"save\", 4) == 0)\n    {\n        QDBSaver  qdb;\n        qdb.Save(g_config.rdbfullname.c_str());\n    }\n\n    Server::Instance()->Terminate();\n    return QError_ok;\n}\n\n\nQError ping(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    FormatSingle(\"PONG\", 4, reply);\n    return QError_ok;\n}\n\nQError echo(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    FormatBulk(params[1], reply);\n    return QError_ok;\n}\n\nvoid OnMemoryInfoCollect(UnboundedBuffer& res)\n{\n    // memory info\n    auto minfo = getMemoryInfo();\n\n    char buf[1024];\n    int n = snprintf(buf, sizeof buf - 1,\n                 \"# Memory\\r\\n\"\n                 \"used_memory_peak:%lu\\r\\n\"\n                 \"used_memory:%lu\\r\\n\"\n                 \"used_memory_human:%sMB\\r\\n\"\n                 \"used_memory_rss_peak:%lu\\r\\n\"\n                 \"used_memory_rss:%lu\\r\\n\"\n                 \"used_memory_rss_human:%sMB\\r\\n\"\n                 \"used_memory_lock:%lu\\r\\n\"\n                 \"used_memory_swap:%lu\\r\\n\"\n                 , minfo[VmPeak]\n                 , minfo[VmSize]\n                 , std::to_string(minfo[VmSize] / 1024.0f / 1024.0f).data()\n                 , minfo[VmHWM]\n                 , minfo[VmRSS]\n                 , std::to_string(minfo[VmRSS] / 1024.0f / 1024.0f).data()\n                 , minfo[VmLck]\n                 , minfo[VmSwap]\n            );\n    \n    if (!res.IsEmpty())\n        res.PushData(\"\\r\\n\", 2);\n\n    res.PushData(buf, n);\n}\n\nvoid OnServerInfoCollect(UnboundedBuffer& res)\n{\n    char buf[1024];\n    \n    // server\n    struct utsname name;\n    uname(&name);\n    int n = snprintf(buf, sizeof buf - 1,\n                 \"# Server\\r\\n\"\n                 \"redis_mode:standalone\\r\\n\" // not cluster node yet\n                 \"os:%s %s %s\\r\\n\"\n                 \"run_id:%s\\r\\n\"\n                 \"hz:%d\\r\\n\"\n                 \"tcp_port:%hu\\r\\n\"\n                 , name.sysname, name.release, name.machine\n                 , g_config.runid.data()\n                 , g_config.hz\n                 , g_config.port);\n    \n    if (!res.IsEmpty())\n        res.PushData(\"\\r\\n\", 2);\n\n    res.PushData(buf, n);\n}\n    \n    \nvoid OnClientInfoCollect(UnboundedBuffer& res)\n{\n    char buf[1024];\n\n    int n = snprintf(buf, sizeof buf - 1,\n                 \"# Clients\\r\\n\"\n                 \"connected_clients:%lu\\r\\n\"\n                 \"blocked_clients:%lu\\r\\n\"\n                 , Server::Instance()->TCPSize()\n                 , QSTORE.BlockedSize());\n    \n    \n    if (!res.IsEmpty())\n        res.PushData(\"\\r\\n\", 2);\n\n    res.PushData(buf, n);\n}\n\nQError info(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    UnboundedBuffer res;\n\n    extern Delegate<void (UnboundedBuffer& )> g_infoCollector;\n    g_infoCollector(res);\n    \n    FormatBulk(res.ReadAddr(), res.ReadableSize(), reply);\n    return QError_ok;\n}\n\n\nQError monitor(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QClient::AddCurrentToMonitor();\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError auth(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (g_config.CheckPassword(params[1]))\n    {\n        QClient::Current()->SetAuth();\n        FormatOK(reply);\n    }\n    else\n    {\n        ReplyError(QError_errAuth, reply);\n    }\n    \n    return QError_ok;\n}\n\nQError slowlog(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params[1] == \"len\")\n    {\n        FormatInt(static_cast<long>(QSlowLog::Instance().GetLogsCount()), reply);\n    }\n    else if (params[1] == \"reset\")\n    {\n        QSlowLog::Instance().ClearLogs();\n        FormatOK(reply);\n    }\n    else if (params[1] == \"get\")\n    {\n        const long limit = static_cast<long>(QSlowLog::Instance().GetLogsCount());\n        long realCnt = limit;\n        if (params.size() == 3)\n        {\n            if (!Strtol(params[2].c_str(), params[2].size(), &realCnt))\n            {\n                ReplyError(QError_syntax, reply);\n                return QError_syntax;\n            }\n        }\n        \n        if (realCnt > limit)\n            realCnt = limit;\n        \n        PreFormatMultiBulk(realCnt, reply);\n        for (const auto& item : QSlowLog::Instance().GetLogs())\n        {\n            if (realCnt -- == 0)\n                break;\n            \n            PreFormatMultiBulk(2, reply);\n            FormatInt(static_cast<long>(item.used), reply);\n            \n            PreFormatMultiBulk(static_cast<long>(item.cmds.size()), reply);\n            for (const auto& c : item.cmds)\n            {\n                FormatBulk(c, reply);\n            }\n        }\n    }\n    else\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n    \n    return QError_ok;\n}\n\n\n// Config options get/set\n//\nenum ConfigType {\n    Config_string,\n    Config_bool,\n    Config_int,\n    Config_int64,\n};\n\nstruct ConfigInfo\n{\n    ConfigType type;\n    bool canModify;\n    void* value;\n};\n\n                    \n// TODO sanity check: use function setter\nstd::map<QString, ConfigInfo> configOptions = {\n    {\"appendonly\", {Config_bool, true, &g_config.appendonly }},\n    {\"bind\", {Config_string, false, &g_config.ip}},\n    {\"dbfilename\", {Config_string, true, &g_config.rdbfullname}},\n    {\"databases\", {Config_int, false, &g_config.databases}},\n    {\"daemonize\", {Config_bool, false, &g_config.daemonize}},\n    {\"hz\", {Config_int, false, &g_config.hz}},\n    {\"logfile\", {Config_string, false, &g_config.logdir}},\n    {\"loglevel\",  {Config_string, true, &g_config.loglevel}},\n    {\"masterauth\", {Config_string, true, &g_config.masterauth}},\n    {\"maxclients\", {Config_int, true, &g_config.maxclients}},\n    {\"port\", {Config_int, false, &g_config.port}},\n    {\"requirepass\", {Config_string, true, &g_config.password}},\n    {\"rdbchecksum\", {Config_bool, false, &g_config.rdbchecksum}},\n    {\"rdbcompression\", {Config_bool, false, &g_config.rdbcompression}},\n    {\"slowlog-log-slower-than\", {Config_int, true, &g_config.slowlogtime}},\n    {\"slowlog-max-len\", {Config_int, true, &g_config.slowlogmaxlen}},\n    {\"slaveof\", {Config_string, false, &g_config.masterIp}},\n    {\"maxmemory\", {Config_int64, true, &g_config.maxmemory}},\n    {\"maxmemorySamples\", {Config_int, true, &g_config.maxmemorySamples}},\n    {\"maxmemory-noevict\", {Config_bool, true, &g_config.noeviction}},\n    {\"backend\", {Config_int, false, &g_config.backend}},\n    {\"backendhz\", {Config_int, false, &g_config.backendHz}},\n};\n\nstatic std::vector<QString> GetConfig(const QString& option)\n{\n    std::vector<QString>  res;\n    std::vector<std::map<QString, ConfigInfo>::const_iterator> iters;\n\n    if (NotGlobRegex(option.data(), option.size()))\n    {\n        auto it = configOptions.find(option);\n        if (it == configOptions.end())\n            return  res;\n\n        iters.push_back(it);\n    }\n    else\n    {\n        // try glob match\n        for (auto it(configOptions.begin()); it != configOptions.end(); ++ it) \n        {\n            if (glob_match(option, it->first))\n                iters.push_back(it);\n        }\n    }\n\n    for (const auto& it : iters)\n    {\n        res.push_back(it->first); // push option\n\n        // push value\n        switch (it->second.type)\n        {\n            case Config_bool:\n                if (*(bool*)(it->second.value))\n                    res.push_back(\"true\");\n                else\n                    res.push_back(\"false\");\n                break;\n\n            case Config_string:\n                res.push_back(*(const QString*)it->second.value);\n                break;\n\n            case Config_int:\n            case Config_int64:\n                {\n                    int64_t val = 0;\n                    if (it->second.type == Config_int)\n                        val = *(int*)it->second.value;\n                    else\n                        val = *(int64_t*)it->second.value;\n\n                    char buf[16] = \"\";\n                    Number2Str(buf, sizeof buf, val);\n                    res.push_back(buf);\n                }\n\n                break;\n\n            default:\n                assert(!!!\"invalid type\");\n        }\n    }\n\n    return res;\n}\n\nstatic QError SetConfig(const QString& option, const QString& value)\n{\n    auto it = configOptions.find(option);\n    if (it == configOptions.end())\n        return QError_syntax;\n\n    if (!it->second.canModify)\n        return QError_syntax;\n        \n    // set option value\n    switch (it->second.type)\n    {\n        case Config_bool:\n            *(bool*)(it->second.value) = (value == \"true\");\n            break;\n\n        case Config_string:\n            *(QString*)it->second.value = value;\n            break;\n\n        case Config_int:\n        case Config_int64:\n            {\n                long val = 0;\n                if (Strtol(value.data(), value.size(), &val))\n                {\n                    if (it->second.type == Config_int)\n                        *(int*)it->second.value = static_cast<int>(val);\n                    else\n                        *(int64_t*)it->second.value = static_cast<int64_t>(val);\n\n                    // ugly... process slow log option\n                    if (option.find(\"slowlog\") == 0)\n                    {\n                        QSlowLog::Instance().SetThreshold(g_config.slowlogtime);\n                        QSlowLog::Instance().SetLogLimit(static_cast<std::size_t>(g_config.slowlogmaxlen));\n                    }\n                }\n                else\n                {\n                    return QError_syntax;\n                }\n            }\n                \n            break;\n\n        default:\n            assert(!!!\"invalid type\");\n    }\n\n    return QError_ok;\n}\n\nQError config(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    // at least 3 params\n    if (strncasecmp(params[1].c_str(), \"get\", 3) == 0)\n    {\n        auto res = GetConfig(params[2]);\n        PreFormatMultiBulk(res.size(), reply);\n        for (const auto& e : res)\n        {\n            FormatBulk(e, reply);\n        }\n    }\n    else if (strncasecmp(params[1].c_str(), \"set\", 3) == 0)\n    {\n        if (params.size() != 4)\n        {\n            ReplyError(QError_param, reply);\n            return QError_param;\n        }\n\n        auto err = SetConfig(params[2], params[3]);\n        if (err == QError_ok)\n        {\n            FormatOK(reply);\n        }\n        else\n        {\n            const char* format = \"-ERR Invalid argument '%s' for CONFIG SET '%s'\\r\\n\";\n            char info[128];\n            auto len = snprintf(info, sizeof info, format, params[3].data(), params[2].data());\n\n            reply->PushData(info, len);\n        }\n\n        return err;\n    }\n    else\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n\n    return   QError_ok;\n}\n    \n}\n\n"
  },
  {
    "path": "QedisCore/QSet.cc",
    "content": "#include \"QSet.h\"\n#include \"QStore.h\"\n#include \"QClient.h\"\n#include <cassert>\n\nnamespace qedis\n{\n\nQObject QObject::CreateSet()\n{\n    QObject set(QType_set);\n    set.Reset(new QSet);\n\n    return set;\n}\n\n#define GET_SET(setname)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(setname, value, QType_set);  \\\n    if (err != QError_ok)  {  \\\n         if (err == QError_notExist)    \\\n             FormatNull(reply); \\\n         else   \\\n             ReplyError(err, reply);    \\\n        return err;  \\\n    }\n\n#define GET_OR_SET_SET(setname)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(setname, value, QType_set);  \\\n    if (err != QError_ok && err != QError_notExist)  {  \\\n        ReplyError(err, reply); \\\n        return err;  \\\n    }   \\\n    if (err == QError_notExist) { \\\n        value = QSTORE.SetValue(setname, QObject::CreateSet());  \\\n    }\n\nstatic bool RandomMember(const QSet& set, QString& res)\n{\n    QSet::const_local_iterator it = RandomHashMember(set);\n\n    if (it != QSet::const_local_iterator())\n    {\n        res = *it;\n        return true;\n    }\n\n    return false;\n}\n\nQError spop(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n\n    auto set = value->CastSet();\n    QString res;\n    if (RandomMember(*set, res))\n    {\n        FormatBulk(res, reply);\n        set->erase(res);\n        if (set->empty())\n            QSTORE.DeleteKey(params[1]);\n\n        std::vector<QString> translated;\n        translated.push_back(\"srem\");\n        translated.push_back(params[1]);\n        translated.push_back(res);\n        \n        QClient::Current()->RewriteCmd(translated);\n    }\n    else\n    {\n        FormatNull(reply);\n        return QError_notExist;\n    }\n    \n    return QError_ok;\n}\n\n\nQError srandmember(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n\n    auto set = value->CastSet();\n    QString res;\n    if (RandomMember(*set, res))\n    {\n        FormatBulk(res, reply);\n    }\n    else\n    {\n        FormatNull(reply);\n    }\n\n    return QError_ok;\n}\n\nQError sadd(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_SET(params[1]);\n    \n    int res = 0;\n    auto set = value->CastSet();\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        if (set->insert(params[i]).second)\n            ++ res;\n    }\n    \n    FormatInt(res, reply);\n    return QError_ok;\n}\n\nQError  scard(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n\n    auto set = value->CastSet();\n    long size = static_cast<long>(set->size());\n    \n    FormatInt(size, reply);\n    return QError_ok;\n}\n\nQError srem(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n\n    auto set = value->CastSet();\n    int res = 0;\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        if (set->erase(params[i]) != 0)\n            ++ res;\n    }\n    \n    if (set->empty())\n        QSTORE.DeleteKey(params[1]);\n    \n    FormatInt(res, reply);\n    return QError_ok;\n}\n\nQError sismember(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n    \n    auto set = value->CastSet();\n    long res = static_cast<long>(set->count(params[2]));\n    \n    FormatInt(res, reply);\n    return QError_ok;\n}\n\nQError smembers(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n\n    auto set = value->CastSet();\n    PreFormatMultiBulk(set->size(), reply);\n    for (const auto& member : *set)\n        FormatBulk(member, reply);\n\n    return QError_ok;\n}\n\nQError smove(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SET(params[1]);\n    \n    auto set = value->CastSet();\n    int ret = static_cast<int>(set->erase(params[3]));\n    if (ret != 0)\n    {\n        QObject* dst;\n        err = QSTORE.GetValueByType(params[2], dst, QType_set);\n        if (err == QError_notExist)\n        {\n            err = QError_ok;\n            QObject val(QObject::CreateSet());\n            dst = QSTORE.SetValue(params[2], std::move(val));\n        }\n        \n        if (err == QError_ok)\n        {\n            auto dset = dst->CastSet();\n            dset->insert(params[3]);\n        }\n    }\n    \n    FormatInt(ret, reply);\n    return err;\n}\n\n\nQSet& QSet_diff(const QSet& l, const QSet& r, QSet& result)\n{\n    for (const auto& le : l)\n    {\n        if (r.count(le) == 0)\n        {\n            result.insert(le);\n        }\n    }\n\n    return result;\n}\n\nQSet& QSet_inter(const QSet& l, const QSet& r, QSet& result)\n{\n    for (const auto& le : l)\n    {\n        if (r.count(le) != 0)\n        {\n            result.insert(le);\n        }\n    }\n\n    return result;\n}\n\n\nQSet& QSet_union(const QSet& l, const QSet& r, QSet& result)\n{\n    for (const auto& re : r)\n    {\n        result.insert(re);\n    }\n\n    for (const auto& le : l)\n    {\n        result.insert(le);\n    }\n    \n    return result;\n}\n\nenum SetOperation\n{\n    SetOperation_diff,\n    SetOperation_inter,\n    SetOperation_union,\n};\n\nstatic void  _set_operation(const std::vector<QString>& params,\n                            size_t offset,\n                            QSet& res,\n                            SetOperation oper)\n{\n    QObject*  value;\n    QError err = QSTORE.GetValueByType(params[offset], value, QType_set);\n    if (err != QError_ok && oper != SetOperation_union)\n        return;\n\n    auto set = value->CastSet();\n    if (set)\n        res = *set;\n    \n    for (size_t i = offset + 1; i < params.size(); ++ i)\n    {\n        QObject*  val;\n        QError err = QSTORE.GetValueByType(params[i], val, QType_set);\n        if (err != QError_ok)\n        {\n            if (oper == SetOperation_inter)\n            {\n                res.clear();\n                return;\n            }\n            continue;\n        }\n        \n        QSet tmp;\n        auto r = val->CastSet();\n        if (oper == SetOperation_diff)\n            QSet_diff(res, *r, tmp);\n        else if (oper == SetOperation_inter)\n            QSet_inter(res, *r, tmp);\n        else if (oper == SetOperation_union)\n            QSet_union(res, *r, tmp);\n        \n        res.swap(tmp);\n        \n        if (oper != SetOperation_union && res.empty())\n            return;\n    }\n}\n\nQError  sdiffstore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject obj(QObject::CreateSet());\n    auto res = obj.CastSet();\n    QSTORE.SetValue(params[1], std::move(obj));\n\n    _set_operation(params, 2, *res, SetOperation_diff);\n\n    FormatInt(static_cast<long>(res->size()), reply);\n    return QError_ok;\n}\n\nQError sdiff(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QSet res;\n    _set_operation(params, 1, res, SetOperation_diff);\n    \n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& elem : res)\n        FormatBulk(elem, reply);\n    \n    return QError_ok;\n}\n\n\nQError sinter(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QSet res;\n    _set_operation(params, 1, res, SetOperation_inter);\n    \n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& elem : res)\n        FormatBulk(elem, reply);\n    \n    return QError_ok;\n}\n\nQError  sinterstore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject obj(QObject::CreateSet());\n    auto res = obj.CastSet();\n    QSTORE.SetValue(params[1], std::move(obj));\n\n    _set_operation(params, 2, *res, SetOperation_inter);\n\n    FormatInt(static_cast<long>(res->size()), reply);\n    return QError_ok;\n}\n\n\nQError  sunion(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QSet res;\n    _set_operation(params, 1, res, SetOperation_union);\n    \n    PreFormatMultiBulk(res.size(), reply);\n    for (const auto& elem : res)\n        FormatBulk(elem, reply);\n    \n    return QError_ok;\n}\n\nQError  sunionstore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject obj(QObject::CreateSet());\n    auto res = obj.CastSet();\n    QSTORE.SetValue(params[1], std::move(obj));\n\n    _set_operation(params, 2, *res, SetOperation_union);\n\n    FormatInt(static_cast<long>(res->size()), reply);\n    return QError_ok;\n}\n\nsize_t SScanKey(const QSet& qset, size_t cursor, size_t count, std::vector<QString>& res)\n{\n    if (qset.empty())\n        return 0;\n    \n    std::vector<QSet::const_local_iterator>  iters;\n    size_t newCursor = ScanHashMember(qset, cursor, count, iters);\n    \n    res.reserve(iters.size());\n    for (auto it : iters)\n        res.push_back(*it);\n    \n    return newCursor;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QSet.h",
    "content": "#ifndef BERT_QSET_H\n#define BERT_QSET_H\n\n#include \"QHelper.h\"\n#include <unordered_set>\n\nnamespace qedis\n{\n\nusing QSet = std::unordered_set<QString,\n        my_hash,\n        std::equal_to<QString> >;\n\nsize_t   SScanKey(const QSet& qset, size_t cursor, size_t count, std::vector<QString>& res);\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QSlaveClient.cc",
    "content": "#include \"QSlaveClient.h\"\n#include \"QConfig.h\"\n#include \"QCommon.h\"\n#include \"Log/Logger.h\"\n\nnamespace qedis\n{\n\nvoid QSlaveClient::OnConnect()\n{\n    std::string cmd = BuildInlineRequest(\"slaveof \", g_config.ip, std::to_string(g_config.port));\n    INF << \"Send to slave cmd \" << cmd;\n\n    SendPacket(cmd.data(), cmd.size());\n    \n    auto wk = std::weak_ptr<QSlaveClient>(std::static_pointer_cast<QSlaveClient>(this->shared_from_this()));\n    Timer* timer = TimerManager::Instance().CreateTimer();\n    timer->Init(3 * 1000, 1);\n    timer->SetCallback([wk]() {\n        auto me = wk.lock();\n        if (me) {\n            USR << \"OnTimer close \" << me->GetPeerAddr().ToString();\n            me->OnError();\n        }\n    });\n    TimerManager::Instance().AsyncAddTimer(timer);\n}\n    \nPacketLength QSlaveClient::_HandlePacket(const char* msg, std::size_t len)\n{\n    return static_cast<PacketLength>(len);\n}\n\n}\n\n"
  },
  {
    "path": "QedisCore/QSlaveClient.h",
    "content": "#ifndef BERT_QSLAVECLIENT_H\n#define BERT_QSLAVECLIENT_H\n\n#include \"StreamSocket.h\"\n\nnamespace qedis\n{\n\nclass QSlaveClient : public StreamSocket\n{\npublic:\n    void OnConnect() override;\nprivate:\n    PacketLength _HandlePacket(const char* msg, std::size_t len) override;\n};\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QSlowLog.cc",
    "content": "#include <sys/time.h>\n#include <fstream>\n#include <sstream>\n\n#include \"Log/Logger.h\"\n#include \"QSlowLog.h\"\n\nnamespace qedis\n{\n\nQSlowLog& QSlowLog::Instance()\n{\n    static QSlowLog slog;\n\n    return slog;\n}\n\nQSlowLog::QSlowLog() : threshold_(0), logger_(nullptr)\n{\n}\n\nQSlowLog::~QSlowLog()\n{\n}\n\nvoid QSlowLog::SetThreshold(unsigned int v)\n{\n    threshold_ = v;\n}\n\nvoid QSlowLog::SetLogLimit(std::size_t maxCount)\n{\n    logMaxCount_ = maxCount;\n}\n\nvoid QSlowLog::Begin()\n{\n    if (!threshold_)\n        return;\n\n    timeval  begin;\n    gettimeofday(&begin, 0);\n    beginUs_ = begin.tv_sec * 1000000 + begin.tv_usec;\n}\n\n\n\nvoid QSlowLog::EndAndStat(const std::vector<QString>& cmds)\n{\n    if (!threshold_ || beginUs_ == 0)\n        return;\n    \n    timeval  end;\n    gettimeofday(&end, 0);\n    auto used = end.tv_sec * 1000000 + end.tv_usec - beginUs_;\n    \n    if (used >= threshold_)\n    {\n        if (logger_ == nullptr)\n            logger_ = LogManager::Instance().CreateLog(logALL, logFILE, \"slowlog.qedis\");\n        \n        LOG_INF(logger_) << \"+ Used:(us) \" << used;\n        \n        for (const auto& param : cmds)\n        {\n            LOG_INF(logger_) << param;\n        }\n        \n        if (cmds[0] == \"slowlog\")\n            return;\n        \n        SlowLogItem item;\n        item.used = static_cast<unsigned>(used);\n        item.cmds = cmds;\n        \n        logs_.emplace_front(std::move(item));\n        if (logs_.size() > logMaxCount_)\n            logs_.pop_back();\n    }\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QSlowLog.h",
    "content": "#ifndef BERT_QSLOWLOG_H\n#define BERT_QSLOWLOG_H\n\n#include <vector>\n#include <deque>\n\n#include \"QString.h\"\n\nclass Logger;\n\nnamespace qedis\n{\n\nstruct SlowLogItem\n{\n    unsigned used;\n    std::vector<QString> cmds;\n    \n    SlowLogItem() : used(0)\n    {\n    }\n    \n    SlowLogItem(SlowLogItem&& item) : used(item.used), cmds(std::move(item.cmds))\n    {\n    }\n};\n\nclass  QSlowLog\n{\npublic:\n    static QSlowLog& Instance();\n    \n    QSlowLog(const QSlowLog& ) = delete;\n    void operator= (const QSlowLog& ) = delete;\n\n    void Begin();\n    void EndAndStat(const std::vector<QString>& cmds);\n    \n    void SetThreshold(unsigned int );\n    void SetLogLimit(std::size_t maxCount);\n    \n    void ClearLogs() { logs_.clear(); }\n    std::size_t GetLogsCount() const { return logs_.size(); }\n    const std::deque<SlowLogItem>& GetLogs() const { return logs_; }\n\nprivate:\n    QSlowLog();\n    ~QSlowLog();\n    \n    unsigned int threshold_;\n    long long    beginUs_;\n    Logger*      logger_;\n    \n    std::size_t  logMaxCount_;\n    std::deque<SlowLogItem> logs_;\n    \n};\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QSortedSet.cc",
    "content": "#include \"QSortedSet.h\"\n#include \"QStore.h\"\n#include \"Log/Logger.h\"\n#include <cassert>\n\nnamespace qedis\n{\n\nQSortedSet::Member2Score::iterator  QSortedSet::FindMember(const QString& member)\n{\n    return  members_.find(member);\n}\n\nvoid  QSortedSet::AddMember(const QString& member, double score)\n{\n    assert (FindMember(member) == members_.end());\n        \n    members_.insert(Member2Score::value_type(member, score));\n    scores_[score].insert(member);\n}\n\ndouble    QSortedSet::UpdateMember(const Member2Score::iterator& itMem, double delta)\n{\n    auto oldScore = itMem->second;\n    auto newScore = oldScore + delta;\n    itMem->second = newScore;\n\n    auto itScore(scores_.find(oldScore));\n    assert (itScore != scores_.end());\n\n    size_t ret = itScore->second.erase(itMem->first);\n    assert (ret == 1);\n\n    bool succ = scores_[newScore].insert(itMem->first).second;\n    assert (succ);\n\n    return newScore;\n}\n\nint QSortedSet::Rank(const QString& member) const\n{\n    double    score;\n    auto itMem(members_.find(member));\n    if (itMem != members_.end())\n        score = itMem->second;\n    else\n        return -1;\n\n    int  rank = 0;\n    for (auto it(scores_.begin());\n              it != scores_.end();\n              rank += it->second.size(), ++ it)\n    {\n        if (it->first == score)\n        {\n            auto iter(it->second.begin());\n            for (; iter != it->second.end(); ++ iter, ++ rank)\n            {\n                if (*iter == member)\n                    return rank;\n            }\n            \n            assert (!!!\"Why can not find member\");\n        }\n    }\n            \n    assert (!!!\"Why can not find score\");\n    return  -1;\n}\n\n\nint QSortedSet::RevRank(const QString& member) const\n{\n    int rank = Rank(member);\n    if (rank == -1)\n        return  rank;\n\n    return static_cast<int>(members_.size() - (rank + 1));\n}\n\nbool QSortedSet::DelMember(const QString& member)\n{\n    double score = 0;\n    Member2Score::const_iterator  itMem(members_.find(member));\n    if (itMem != members_.end())\n    {\n        score  = itMem->second;\n        members_.erase(itMem);\n    }\n    else\n    {\n        return false;\n    }\n\n    auto it(scores_.find(score));\n    assert (it != scores_.end());\n            \n    auto num = it->second.erase(member);\n    assert (num == 1);\n\n    return true;\n}\n\nQSortedSet::Member2Score::value_type\nQSortedSet::GetMemberByRank(size_t rank) const\n{\n    if (rank >= members_.size())\n        rank = members_.size() - 1;\n\n    double score = 0;\n    size_t iterRank = 0;\n\n    for ( auto it(scores_.begin());\n          it != scores_.end();\n          iterRank += it->second.size(), ++ it)\n    {\n        if (iterRank + it->second.size() > rank)\n        {\n            assert(iterRank <= rank);\n            \n            score = it->first;\n            auto itMem(it->second.begin());\n            for (; iterRank != rank; ++ iterRank, ++ itMem)\n            {\n            }\n            \n            DBG << \"Get rank \" << rank << \", name \" << itMem->c_str();\n            return std::make_pair(*itMem, score);\n        }\n    }\n\n    return std::make_pair(QString(), score);\n}\n\n\nsize_t QSortedSet::Size() const\n{\n    return members_.size();\n}\n\n\nstd::vector<QSortedSet::Member2Score::value_type >\nQSortedSet::RangeByRank(long start, long end) const\n{\n    AdjustIndex(start, end, Size());\n    if (start > end)\n        return std::vector<Member2Score::value_type >();\n    \n    std::vector<Member2Score::value_type >  res;\n    for (long rank = start; rank <= end; ++ rank)\n    {\n        res.push_back(GetMemberByRank(rank));\n    }\n    \n    return res;\n}\n\nstd::vector<QSortedSet::Member2Score::value_type >\nQSortedSet::RangeByScore(double minScore, double maxScore)\n{\n    if (minScore > maxScore)\n        return std::vector<Member2Score::value_type >();\n    \n    auto itMin = scores_.lower_bound(minScore);\n    if (itMin == scores_.end())\n        return std::vector<Member2Score::value_type >();\n    \n    std::vector<Member2Score::value_type>  res;\n    auto itMax = scores_.upper_bound(maxScore);\n    for (; itMin != itMax; ++ itMin)\n    {\n        for (const auto& e : itMin->second)\n        {\n            res.push_back(std::make_pair(e, itMin->first));\n        }\n    }\n    return  res;\n}\n\nQObject QObject::CreateSSet()\n{\n    QObject obj(QType_sortedSet);\n    obj.Reset(new QSortedSet);\n    return obj;\n}\n\n// commands\n#define GET_SORTEDSET(name)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(name, value, QType_sortedSet);  \\\n    if (err != QError_ok)  {  \\\n        ReplyError(err, reply); \\\n        return err;  \\\n}\n\n#define GET_OR_SET_SORTEDSET(name)  \\\n    QObject* value;  \\\n    QError err = QSTORE.GetValueByType(name, value, QType_sortedSet);  \\\n    if (err != QError_ok && err != QError_notExist)  {  \\\n        ReplyError(err, reply); \\\n        return err;  \\\n    }   \\\n    if (err == QError_notExist) { \\\n        value = QSTORE.SetValue(name, QObject::CreateSSet());  \\\n    }\n\nQError zadd(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 != 0)\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n\n    GET_OR_SET_SORTEDSET(params[1]);\n    \n    size_t newMembers = 0;\n    auto sset = value->CastSortedSet();\n    for (size_t i = 2; i < params.size(); i += 2)\n    {\n        double score = 0;\n        if (!Strtod(params[i].c_str(), params[i].size(), &score))\n        {\n            ReplyError(QError_nan, reply);\n            return QError_nan;\n        }\n\n        auto it = sset->FindMember(params[i+1]);\n        if (it == sset->end())\n        {\n            sset->AddMember(params[i+1], score);\n            ++ newMembers;\n        }\n    }\n\n    FormatInt(newMembers, reply);\n    return   QError_ok;\n}\n\nQError  zcard(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SORTEDSET(params[1]);\n    \n    auto sset = value->CastSortedSet();\n\n    FormatInt(static_cast<long>(sset->Size()), reply);\n    return QError_ok;\n}\n\nQError  zrank(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SORTEDSET(params[1]);\n    \n    auto sset = value->CastSortedSet();\n\n    int rank = sset->Rank(params[2]);\n    if (rank != -1)\n        FormatInt(rank, reply);\n    else\n        FormatNull(reply);\n\n    return QError_ok;\n}\n\nQError zrevrank(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SORTEDSET(params[1]);\n    \n    auto sset = value->CastSortedSet();\n\n    int rrank = sset->RevRank(params[2]);\n    if (rrank != -1)\n        FormatInt(rrank, reply);\n    else\n        FormatNull(reply);\n\n    return QError_ok;\n}\n\nQError zrem(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SORTEDSET(params[1]);\n    \n    auto sset = value->CastSortedSet();\n    long cnt = 0;\n    for (size_t i = 2; i < params.size(); ++ i)\n    {\n        if (sset->DelMember(params[i]))\n            ++ cnt;\n    }\n\n    FormatInt(cnt, reply);\n    return QError_ok;\n}\n\nQError  zincrby(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_OR_SET_SORTEDSET(params[1]);\n\n    double delta;\n    if (!Strtod(params[2].c_str(), params[2].size(), &delta))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    double newScore = delta;\n    auto sset = value->CastSortedSet();\n    auto itMem = sset->FindMember(params[3]);\n    if (itMem == sset->end())\n        sset->AddMember(params[3], delta);\n    else\n        newScore = sset->UpdateMember(itMem, delta);\n\n    FormatInt(newScore, reply);\n    return QError_ok;\n}\n\nQError zscore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    GET_SORTEDSET(params[1]);\n\n    auto sset = value->CastSortedSet();\n    auto itMem = sset->FindMember(params[2]);\n    if (itMem == sset->end())\n        FormatNull(reply);\n    else\n        FormatInt(itMem->second, reply);\n\n    return QError_ok;\n}\n\n\nstatic QError GenericRange(const std::vector<QString>& params, UnboundedBuffer* reply, bool reverse)\n{\n    GET_SORTEDSET(params[1]);\n    \n    bool withScore = false;\n    if (params.size() == 5 && strncasecmp(params[4].c_str(), \"withscores\", 10) == 0)\n    {\n        withScore = true;\n    }\n    else if (params.size() >= 5)\n    {\n        ReplyError(QError_syntax, reply);\n        return QError_syntax;\n    }\n    \n    long start, end;\n    if (!Strtol(params[2].c_str(), params[2].size(), &start) ||\n        !Strtol(params[3].c_str(), params[3].size(), &end))\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n    \n    auto sset = value->CastSortedSet();\n    \n    auto res(sset->RangeByRank(start, end));\n    if (res.empty())\n    {\n        FormatNullArray(reply);\n        return QError_ok;\n    }\n    \n    long nBulk = withScore ? res.size() * 2 : res.size();\n    PreFormatMultiBulk(nBulk, reply);\n    \n    if (!reverse)\n    {\n        for (const auto& s : res)\n        {\n            FormatBulk(s.first, reply);\n            if (withScore)\n            {\n                char score[64];\n                int  len = Double2Str(score, sizeof score, s.second);\n            \n                FormatBulk(score, len, reply);\n            }\n        }\n    }\n    else\n    {\n        for (auto it(res.rbegin()); it != res.rend(); ++ it)\n        {\n            const auto& s = *it;\n            FormatBulk(s.first, reply);\n            if (withScore)\n            {\n                char score[64];\n                int  len = Double2Str(score, sizeof score, s.second);\n                \n                FormatBulk(score, len, reply);\n            }\n        }\n    }\n    \n    return QError_ok;\n}\n\n// zrange key start stop [WITHSCORES]\nQError zrange(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericRange(params, reply, false);\n}\n\n// zrange key start stop [WITHSCORES]\nQError  zrevrange(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericRange(params, reply, true);\n}\n\n\nstatic QError GenericScoreRange(const std::vector<QString>& params, UnboundedBuffer* reply, bool reverse)\n{\n    GET_SORTEDSET(params[1]);\n    \n    bool withScore = false;\n    if (params.size() == 5 && strncasecmp(params[4].c_str(), \"withscores\", 10) == 0)\n    {\n        withScore = true;\n    }\n    else if (params.size() >= 5)\n    {\n        ReplyError(QError_syntax, reply);\n        return  QError_syntax;\n    }\n    \n    long minScore, maxScore;\n    if (!Strtol(params[2].c_str(), params[2].size(), &minScore) ||\n        !Strtol(params[3].c_str(), params[3].size(), &maxScore))\n    {\n        ReplyError(QError_nan, reply);\n        return  QError_nan;\n    }\n    \n    auto sset = value->CastSortedSet();\n    \n    auto res(sset->RangeByScore(minScore, maxScore));\n    if (res.empty())\n    {\n        FormatNull(reply);\n        return QError_ok;\n    }\n    \n    long nBulk = withScore ? res.size() * 2 : res.size();\n    PreFormatMultiBulk(nBulk, reply);\n    \n    if (!reverse)\n    {\n        for (const auto& s : res)\n        {\n            FormatBulk(s.first, reply);\n            if (withScore)\n            {\n                char score[64];\n                int  len = Double2Str(score, sizeof score, s.second);\n                \n                FormatBulk(score, len, reply);\n            }\n        }\n    }\n    else\n    {\n        for (auto it(res.rbegin()); it != res.rend(); ++ it)\n        {\n            const auto& s = *it;\n            FormatBulk(s.first, reply);\n            if (withScore)\n            {\n                char score[64];\n                int  len = Double2Str(score, sizeof score, s.second);\n                \n                FormatBulk(score, len, reply);\n            }\n        }\n    }\n    \n    return QError_ok;\n}\n\nQError  zrangebyscore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericScoreRange(params, reply, false);\n}\n\nQError  zrevrangebyscore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericScoreRange(params, reply, true);\n}\n\nstatic QError GenericRemRange(const std::vector<QString>& params, UnboundedBuffer* reply, bool useRank)\n{\n    GET_SORTEDSET(params[1]);\n    \n    double start, end;\n    if (!Strtod(params[2].c_str(), params[2].size(), &start) ||\n        !Strtod(params[3].c_str(), params[3].size(), &end))\n    {\n        ReplyError(QError_nan, reply);\n        return  QError_nan;\n    }\n    \n    std::vector<QSortedSet::Member2Score::value_type> res;\n    auto sset = value->CastSortedSet();\n    if (useRank)\n    {\n        long lstart = static_cast<long>(start);\n        long lend   = static_cast<long>(end);\n        AdjustIndex(lstart, lend, sset->Size());\n        res = sset->RangeByRank(lstart, lend);\n    }\n    else\n    {\n        res = sset->RangeByScore(start, end);\n    }\n    \n    if (res.empty())\n    {\n        Format0(reply);\n        return QError_ok;\n    }\n    \n    for (const auto& s : res)\n    {\n        bool succ = sset->DelMember(s.first);\n        assert(succ);\n    }\n    \n    if (sset->Size() == 0)\n        QSTORE.DeleteKey(params[1]);\n    \n    FormatInt(static_cast<long>(res.size()), reply);\n    return QError_ok;\n}\n\nQError zremrangebyrank(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericRemRange(params, reply, true);\n}\n\nQError zremrangebyscore(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return GenericRemRange(params, reply, false);\n}\n    \n}\n"
  },
  {
    "path": "QedisCore/QSortedSet.h",
    "content": "#ifndef BERT_QSORTEDSET_H\n#define BERT_QSORTEDSET_H\n\n#include \"QString.h\"\n#include \"QHelper.h\"\n#include <map>\n#include <set>\n#include <vector>\n#include <unordered_map>\n\nnamespace qedis\n{\n\nclass QSortedSet\n{\npublic:\n    using Members = std::set<QString>;\n    using Score2Members = std::map<double, Members>;\n\n    using Member2Score = std::unordered_map<QString, double,\n                                            my_hash,\n                                            std::equal_to<QString> >;\n\n    Member2Score::iterator FindMember(const QString& member);\n    Member2Score::const_iterator begin() const {  return members_.begin(); };\n    Member2Score::iterator begin() {  return members_.begin(); };\n    Member2Score::const_iterator end() const {  return members_.end(); };\n    Member2Score::iterator end() {  return members_.end(); };\n    void    AddMember   (const QString& member, double score);\n    double  UpdateMember(const Member2Score::iterator& itMem, double delta);\n\n    int     Rank        (const QString& member) const;// 0-based\n    int     RevRank     (const QString& member) const;// 0-based\n    bool    DelMember   (const QString& member);\n    Member2Score::value_type\n        GetMemberByRank(std::size_t rank) const;\n    \n    std::vector<Member2Score::value_type >\n        RangeByRank(long start, long end) const;\n\n    std::vector<Member2Score::value_type >\n        RangeByScore(double minScore, double maxScore);\n\n    std::size_t Size() const;\n\nprivate:\n    Score2Members   scores_;\n    Member2Score    members_;\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QStore.cc",
    "content": "#include \"QStore.h\"\n#include \"QClient.h\"\n#include \"QConfig.h\"\n#include \"QAOF.h\"\n#include \"QMulti.h\"\n#include \"Log/Logger.h\"\n#include \"QLeveldb.h\"\n#include <limits>\n#include <cassert>\n\n\nnamespace qedis\n{\n\nuint32_t QObject::lruclock = static_cast<uint32_t>(::time(nullptr));\n    \n\nQObject::QObject(QType t) : type(t)\n{\n    switch (type)\n    {\n        case QType_list:\n            encoding = QEncode_list;\n            break;\n                    \n        case QType_set:\n            encoding = QEncode_set;\n            break;\n                    \n        case QType_sortedSet:\n            encoding = QEncode_sset;\n            break;\n                    \n        case QType_hash:\n            encoding = QEncode_hash;\n            break;\n                    \n        default:\n            encoding = QEncode_invalid;\n            break;\n    }\n\n    lru = 0;\n    value = nullptr;\n}\n        \nQObject::~QObject()\n{\n    _FreeValue();\n}\n    \nvoid QObject::Clear()\n{\n    _FreeValue();\n            \n    type = QType_invalid;\n    encoding = QEncode_invalid;\n    lru = 0;\n    value = nullptr;\n}\n        \nvoid QObject::Reset(void* newvalue)\n{\n    _FreeValue();\n    value = newvalue;\n}\n        \nQObject::QObject(QObject&& obj) :\n    type(QType_invalid),\n    encoding(QEncode_invalid),\n    lru(0),\n    value(nullptr)\n{\n    _MoveFrom(std::move(obj));\n}\n        \nQObject& QObject::operator= (QObject&& obj)\n{\n    _MoveFrom(std::move(obj));\n    return *this;\n}\n    \nvoid QObject::_MoveFrom(QObject&& obj)\n{\n    this->Reset();\n            \n    this->encoding = obj.encoding;\n    this->type = obj.type;\n    this->value = obj.value;\n    this->lru = obj.lru;\n            \n    obj.encoding = QEncode_invalid;\n    obj.type = QType_invalid;\n    obj.value = nullptr;\n    obj.lru = 0;\n}\n        \nvoid QObject::_FreeValue()\n{\n    switch (encoding)\n    {\n        case QEncode_raw:\n            delete CastString();\n            break;\n                    \n        case QEncode_list:\n            delete CastList();\n            break;\n                    \n        case QEncode_set:\n            delete CastSet();\n            break;\n                    \n        case QEncode_sset:\n            delete CastSortedSet();\n            break;\n                    \n        case QEncode_hash:\n            delete CastHash();\n            break;\n                    \n        default:\n            break;\n    }\n}\n\nint QStore::dirty_ = 0;\n\nvoid QStore::ExpiresDB::SetExpire(const QString& key, uint64_t when)\n{\n    expireKeys_[key] = when;\n}\n\nint64_t QStore::ExpiresDB::TTL(const QString& key, uint64_t now)\n{\n    if (!QSTORE.ExistsKey(key))\n        return ExpireResult::notExist;\n\n    ExpireResult ret = ExpireIfNeed(key, now);\n    switch (ret)\n    {\n        case ExpireResult::expired:\n        case ExpireResult::persist:\n            return ret;\n            \n        default:\n            break;\n    }\n    \n    auto it(expireKeys_.find(key));\n    return static_cast<int64_t>(it->second - now);\n}\n\nbool QStore::ExpiresDB::ClearExpire(const QString& key)\n{\n    return ExpireResult::expired == ExpireIfNeed(key, std::numeric_limits<uint64_t>::max());\n}\n\nQStore::ExpireResult  QStore::ExpiresDB::ExpireIfNeed(const QString& key, uint64_t now)\n{\n    auto    it(expireKeys_.find(key));\n    \n    if (it != expireKeys_.end())\n    {\n        if (it->second > now)\n            return ExpireResult::notExpire;\n        \n        WRN << \"Delete timeout key \" << it->first;\n        QSTORE.DeleteKey(it->first);\n        expireKeys_.erase(it);\n        return ExpireResult::expired;\n    }\n    \n    return  ExpireResult::persist;\n}\n\nint QStore::ExpiresDB::LoopCheck(uint64_t now)\n{\n    const int kMaxDel = 100;\n    const int kMaxCheck = 2000;\n\n    int  nDel = 0;\n    int  nLoop = 0;\n\n    for (auto  it = expireKeys_.begin();\n               it!= expireKeys_.end() && nDel < kMaxDel && nLoop < kMaxCheck;\n               ++ nLoop)\n    {\n        if (it->second <= now)\n        {\n            // time to delete\n            INF << \"LoopCheck try delete key:\" << it->first;\n            \n            std::vector<QString> params{\"del\", it->first};\n            Propogate(params);\n            \n            QSTORE.DeleteKey(it->first);\n            expireKeys_.erase(it ++);\n\n            ++ nDel;\n        }\n        else\n        {\n            ++ it;\n        }\n    }\n\n    return nDel;\n\n}\n\n\nbool QStore::BlockedClients::BlockClient(const QString& key,\n                                         QClient* client,\n                                         uint64_t timeout,\n                                         ListPosition pos,\n                                         const QString* target)\n{\n    if (!client->WaitFor(key, target))\n    {\n        ERR << key << \" is already waited by \" << client->GetName();\n        return  false;\n    }\n        \n    Clients& clients = blockedClients_[key];\n    clients.push_back(Clients::value_type(std::static_pointer_cast<QClient>(client->shared_from_this()), timeout, pos));\n    \n    INF << key << \" is waited by \" << client->GetName() << \", timeout \" << timeout;\n    return true;\n}\n\nsize_t QStore::BlockedClients::UnblockClient(QClient* client)\n{\n    size_t n = 0;\n    const auto& keys = client->WaitingKeys();\n    \n    for (const auto& key : keys)\n    {\n        Clients&  clients = blockedClients_[key];\n        assert(!clients.empty());\n        \n        for (auto it(clients.begin()); it != clients.end(); ++ it)\n        {\n            auto  cli(std::get<0>(*it).lock());\n            if (cli && cli.get() == client)\n            {\n                INF << \"unblock \" << client->GetName() << \" for key \" << key;\n                clients.erase(it);\n                \n                ++ n;\n                break;\n            }\n        }\n    }\n    \n    client->ClearWaitingKeys();\n    return n;\n}\n\n\nsize_t  QStore::BlockedClients::ServeClient(const QString& key, const PLIST& list)\n{\n    assert(!list->empty());\n    \n    auto it = blockedClients_.find(key);\n    if (it == blockedClients_.end())\n        return 0;\n\n    Clients& clients = it->second;\n    if (clients.empty())\n        return 0;\n    \n    size_t nServed = 0;\n        \n    while (!list->empty() && !clients.empty())\n    {\n        auto  cli(std::get<0>(clients.front()).lock());\n        auto  pos(std::get<2>(clients.front()));\n\n        if (cli)\n        {\n            bool  errorTarget     = false;\n            const QString& target = cli->GetTarget();\n            \n            QObject* dst = nullptr;\n\n            if (!target.empty())\n            {\n                INF << list->front() << \" is try lpush to target list \" << target;\n                \n                // check target list\n                QError err = QSTORE.GetValueByType(target, dst, QType_list);\n                if (err != QError_ok)\n                {\n                    if (err != QError_notExist)\n                    {\n                        UnboundedBuffer reply;\n                        ReplyError(err, &reply);\n                        cli->SendPacket(reply);\n                        errorTarget = true;\n                    }\n                    else\n                    {\n                        dst = QSTORE.SetValue(target, QObject::CreateList());\n                    }\n                }\n            }\n            \n            if (!errorTarget)\n            {\n                if (dst)\n                {\n                    auto dstlist = dst->CastList();\n                    dstlist->push_front(list->back());\n                    INF << list->front() << \" success lpush to target list \" << target;\n\n                    std::vector<QString> params{\"lpush\", target, list->back()};\n                    Propogate(params);\n                }\n                \n                UnboundedBuffer reply;\n            \n                if (!dst)\n                {\n                    PreFormatMultiBulk(2, &reply);\n                    FormatBulk(key, &reply);\n                }\n\n                if (pos == ListPosition::head)\n                {\n                    FormatBulk(list->front(), &reply);\n                    list->pop_front();\n\n                    std::vector<QString> params{\"lpop\", key};\n                    Propogate(params);\n                }\n                else\n                {\n                    FormatBulk(list->back(), &reply);\n                    list->pop_back();\n\n                    std::vector<QString> params{\"rpop\", key};\n                    Propogate(params);\n                }\n                \n                cli->SendPacket(reply);\n                INF << \"Serve client \" << cli->GetName() << \" list key : \" << key;\n            }\n            \n            UnblockClient(cli.get());\n            ++ nServed;\n        }\n        else\n        {\n            clients.pop_front();\n        }\n    }\n    \n    return nServed;\n}\n    \nint QStore::BlockedClients::LoopCheck(uint64_t now)\n{\n    int  n = 0;\n\n    for (auto  it(blockedClients_.begin());\n         it != blockedClients_.end() && n < 100; )\n    {\n        Clients&  clients = it->second;\n        for (auto cli(clients.begin()); cli != clients.end(); )\n        {\n            if (std::get<1>(*cli) < now) // timeout\n            {\n                ++ n;\n                \n                const QString& key = it->first;\n                auto  scli(std::get<0>(*cli).lock());\n                if (scli && scli->WaitingKeys().count(key))\n                {\n                    INF << scli->GetName() << \" is timeout for waiting key \" << key;\n                    UnboundedBuffer  reply;\n                    FormatNull(&reply);\n                    scli->SendPacket(reply);\n                    scli->ClearWaitingKeys();\n                }\n\n                clients.erase(cli ++);\n            }\n            else\n            {\n                ++ cli;\n            }\n        }\n        \n        if (clients.empty())\n        {\n            blockedClients_.erase(it ++);\n        }\n        else\n        {\n            ++ it;\n        }\n    }\n    \n    return n;\n}\n\n\nQStore& QStore::Instance()\n{\n    static QStore store;\n    return store;\n}\n\nvoid  QStore::Init(int dbNum)\n{\n    if (dbNum < 1)\n        dbNum = 1;\n    else if (dbNum > kMaxDbNum)\n        dbNum = kMaxDbNum;\n    \n    store_.resize(dbNum);\n    expiresDb_.resize(dbNum);\n    blockedClients_.resize(dbNum);\n}\n\nint  QStore::LoopCheckExpire(uint64_t now)\n{\n    return expiresDb_[dbno_].LoopCheck(now);\n}\n\nint  QStore::LoopCheckBlocked(uint64_t now)\n{\n    return blockedClients_[dbno_].LoopCheck(now);\n}\n\n\nint QStore::SelectDB(int dbno)\n{\n    if (dbno == dbno_)\n        return  dbno_;\n    \n    if (dbno >= 0 && dbno < static_cast<int>(store_.size()))\n    {\n        int oldDb = dbno_;\n\n        dbno_ = dbno;\n        return oldDb;\n    }\n        \n    return -1;\n}\n\nint  QStore::GetDB() const\n{\n    return dbno_;\n}\n\nconst QObject* QStore::GetObject(const QString& key) const\n{\n    auto db = &store_[dbno_];\n    QDB::const_iterator it(db->find(key));\n    if (it != db->end())\n        return &it->second;\n\n    if (!backends_.empty())\n    {\n        // if it's in dirty list, it must be deleted, wait sync to backend\n        if (waitSyncKeys_[dbno_].count(key))\n            return nullptr;\n\n        // load from leveldb, if has, insert to qedis cache\n        QObject obj = backends_[dbno_]->Get(key);\n        if (obj.type != QType_invalid)\n        {\n            DBG << \"GetKey from leveldb:\" << key;\n\n            QObject& realobj = ((*db)[key] = std::move(obj));\n            realobj.lru = QObject::lruclock;\n\n            // trick: use lru field to store the remain seconds to be expired.\n            unsigned int remainTtlSeconds = obj.lru;\n            if (remainTtlSeconds > 0)\n                SetExpire(key, ::Now() + remainTtlSeconds * 1000);\n\n            return &realobj;\n        }\n    }\n\n    return nullptr;\n}\n\nbool QStore::DeleteKey(const QString& key)\n{\n    auto db = &store_[dbno_];\n    // add to dirty queue\n    if (!waitSyncKeys_.empty())\n    {\n        waitSyncKeys_[dbno_][key] = nullptr; // null implies delete data\n    }\n\n    return db->erase(key) != 0;\n}\n\nbool QStore::ExistsKey(const QString& key) const\n{\n    const QObject* obj = GetObject(key);\n    return obj != nullptr;\n}\n\nQType  QStore::KeyType(const QString& key) const\n{\n    const QObject* obj = GetObject(key);\n    if (!obj)\n        return QType_invalid;\n    \n    return QType(obj->type);\n}\n\nstatic bool RandomMember(const QDB& hash, QString& res, QObject** val)\n{\n    QDB::const_local_iterator it = RandomHashMember(hash);\n    \n    if (it != QDB::const_local_iterator())\n    {\n        res = it->first;\n        if (val) *val = const_cast<QObject*>(&it->second);\n        return true;\n    }\n    \n    return false;\n}\n\nQString QStore::RandomKey(QObject** val) const\n{\n    QString res;\n    if (!store_.empty() && !store_[dbno_].empty())\n        RandomMember(store_[dbno_], res, val);\n\n    return res;\n}\n\nsize_t QStore::ScanKey(size_t cursor, size_t count, std::vector<QString>& res) const\n{\n    if (store_.empty() || store_[dbno_].empty())\n        return 0;\n\n    std::vector<QDB::const_local_iterator> iters;\n    size_t newCursor = ScanHashMember(store_[dbno_], cursor, count, iters);\n\n    res.reserve(iters.size());\n    for (auto it : iters)\n        res.push_back(it->first);\n\n    return newCursor;\n}\n\nQError  QStore::GetValue(const QString& key, QObject*& value, bool touch)\n{\n    if (touch)\n        return GetValueByType(key, value);\n    else\n        return GetValueByTypeNoTouch(key, value);\n}\n\nQError  QStore::GetValueByType(const QString& key, QObject*& value, QType type)\n{\n    return _GetValueByType(key, value, type, true);\n}\n\nQError  QStore::GetValueByTypeNoTouch(const QString& key, QObject*& value, QType type)\n{\n    return _GetValueByType(key, value, type, false);\n}\n\nQError  QStore::_GetValueByType(const QString& key, QObject*& value, QType type, bool touch)\n{\n    if (_ExpireIfNeed(key, ::Now()) == ExpireResult::expired)\n        return QError_notExist;\n    \n    auto cobj = GetObject(key);\n    if (cobj)\n    {\n        if (type != QType_invalid && type != QType(cobj->type))\n        {\n            return QError_type;\n        }\n        else\n        {\n            value = const_cast<QObject*>(cobj);\n\n            // Do not update if child process exists\n            extern pid_t g_qdbPid;\n            if (touch && g_rewritePid == -1 && g_qdbPid == -1)\n                value->lru = QObject::lruclock;\n\n            return QError_ok;\n        }\n    }\n    else\n    {\n        return QError_notExist;\n    }\n\n    return  QError_ok; // never here\n}\n\n\nQObject* QStore::SetValue(const QString& key, QObject&& value)\n{\n    auto db = &store_[dbno_];\n    QObject& obj = ((*db)[key] = std::move(value));\n    obj.lru = QObject::lruclock;\n\n    // put this key to sync list\n    if (!waitSyncKeys_.empty())\n        waitSyncKeys_[dbno_][key] = &obj;\n\n    return &obj;\n}\n\nvoid QStore::SetExpire(const QString& key, uint64_t when) const\n{\n    expiresDb_[dbno_].SetExpire(key, when);\n}\n\nvoid QStore::SetExpireAfter(const QString& key, uint64_t ttl) const\n{\n    SetExpire(key, ::Now() + ttl);\n}\n\n\nint64_t QStore::TTL(const QString& key, uint64_t now)\n{\n    return expiresDb_[dbno_].TTL(key, now);\n}\n\nbool QStore::ClearExpire(const QString& key)\n{\n    return expiresDb_[dbno_].ClearExpire(key);\n}\n\nQStore::ExpireResult QStore::_ExpireIfNeed(const QString& key, uint64_t now)\n{\n    return  expiresDb_[dbno_].ExpireIfNeed(key, now);\n}\n\nvoid QStore::InitExpireTimer()\n{\n    for (int i = 0; i < static_cast<int>(expiresDb_.size()); ++ i)\n    {\n        auto timer = TimerManager::Instance().CreateTimer();\n        timer->Init(1);\n        timer->SetCallback([&, i] () {\n                int oldDb = QSTORE.SelectDB(i);\n                QSTORE.LoopCheckExpire(::Now());\n                QSTORE.SelectDB(oldDb);\n        });\n\n        TimerManager::Instance().AddTimer(timer);\n    }\n}\n\nvoid QStore::ResetDb()\n{\n    std::vector<QDB>(store_.size()).swap(store_);\n    std::vector<ExpiresDB>(expiresDb_.size()).swap(expiresDb_);\n    std::vector<BlockedClients>(blockedClients_.size()).swap(blockedClients_);\n    dbno_ = 0;\n}\n\nsize_t QStore::BlockedSize() const\n{\n    size_t s = 0;\n    for (const auto& b : blockedClients_)\n        s += b.Size();\n    \n    return s;\n}\n\nbool    QStore::BlockClient(const QString& key, QClient* client, uint64_t timeout, ListPosition pos, const QString* dstList)\n{\n    return blockedClients_[dbno_].BlockClient(key, client, timeout, pos, dstList);\n}\nsize_t  QStore::UnblockClient(QClient* client)\n{\n    return blockedClients_[dbno_].UnblockClient(client);\n}\nsize_t  QStore::ServeClient(const QString& key, const PLIST& list)\n{\n    return blockedClients_[dbno_].ServeClient(key, list);\n}\n\nvoid    QStore::InitBlockedTimer()\n{\n    for (int i = 0; i < static_cast<int>(blockedClients_.size()); ++ i)\n    {\n        auto timer = TimerManager::Instance().CreateTimer();\n        timer->Init(3);\n        timer->SetCallback([&, i] () {\n                int oldDb = QSTORE.SelectDB(i);\n                QSTORE.LoopCheckBlocked(::Now());\n                QSTORE.SelectDB(oldDb);\n        });\n\n        TimerManager::Instance().AddTimer(timer);\n    }\n}\n\n\nstatic void EvictItems()\n{\n    QObject::lruclock = static_cast<uint32_t>(::time(nullptr));\n    QObject::lruclock &= kMaxLRUValue;\n\n    int currentDb = QSTORE.GetDB();\n    \n    QEDIS_DEFER {\n        QSTORE.SelectDB(currentDb);\n    };\n\n    int tryCnt = 0;\n    size_t usedMem = 0;\n    while (tryCnt ++ < 32 && (usedMem = getMemoryInfo(VmRSS)) > g_config.maxmemory)\n    {\n        if (g_config.noeviction)\n        {\n            WRN << \"noeviction policy, but memory usage exceeds: \" << usedMem;\n            return;\n        }\n\n        for (int dbno = 0; true; ++ dbno)\n        {\n            if (QSTORE.SelectDB(dbno) == -1)\n                break;\n\n            if (QSTORE.DBSize() == 0)\n                continue;\n        \n            QString evictKey;\n            uint32_t choosedIdle = 0;\n            for (int i = 0; i < g_config.maxmemorySamples; ++ i)\n            {\n                QObject* val = nullptr;\n\n                auto key = QSTORE.RandomKey(&val);\n                if (!val) continue;\n                \n                auto idle = EstimateIdleTime(val->lru);\n                if (evictKey.empty() || choosedIdle < idle)\n                {\n                    evictKey = std::move(key);\n                    choosedIdle = idle;\n                }\n            }\n\n            if (!evictKey.empty())\n            {\n                QSTORE.DeleteKey(evictKey);\n                WRN << \"Evict '\" << evictKey << \"' in db \" << dbno << \", idle time: \" << choosedIdle << \", used mem: \" << usedMem;\n            }\n        }\n    }\n}\n\nuint32_t EstimateIdleTime(uint32_t lru)\n{\n    if (lru <= QObject::lruclock)\n        return QObject::lruclock - lru;\n    else\n        return (kMaxLRUValue - lru) + QObject::lruclock;\n}\n\n\nvoid QStore::InitEvictionTimer()\n{\n    auto timer = TimerManager::Instance().CreateTimer();\n    timer->Init(1000); // emit eviction every second.\n    timer->SetCallback([] () {\n        EvictItems();\n    });\n\n    TimerManager::Instance().AddTimer(timer);\n}\n\nvoid QStore::InitDumpBackends()\n{\n    assert (waitSyncKeys_.empty());\n\n    if (g_config.backend == BackEndNone)\n        return;\n\n    if (g_config.backend == BackEndLeveldb)\n    {\n        waitSyncKeys_.resize(store_.size());\n        for (size_t i = 0; i < store_.size(); ++ i)\n        {\n            std::unique_ptr<QLeveldb> db(new QLeveldb);\n            QString dbpath = g_config.backendPath + std::to_string(i);\n            if (!db->Open(dbpath.data()))\n                assert(false);\n            else\n                USR << \"Open leveldb \" << dbpath;\n\n            backends_.push_back(std::move(db));\n        }\n    }\n    else \n    {\n        // ERROR: unsupport backend\n        return;\n    }\n        \n    for (int i = 0; i < static_cast<int>(backends_.size()); ++ i)\n    {\n        auto timer = TimerManager::Instance().CreateTimer();\n        timer->Init(1000 / g_config.backendHz);\n        timer->SetCallback([&, i] () {\n                int oldDb = QSTORE.SelectDB(i);\n                QSTORE.DumpToBackends(i);\n                QSTORE.SelectDB(oldDb);\n        });\n\n        TimerManager::Instance().AddTimer(timer);\n    }\n}\n\nvoid  QStore::DumpToBackends(int dbno)\n{\n    if (static_cast<int>(waitSyncKeys_.size()) <= dbno)\n        return;\n\n    const int kMaxSync = 100;\n    int processed = 0;\n    auto& dirtyKeys = waitSyncKeys_[dbno];\n            \n    uint64_t now = ::Now();\n    for (auto it = dirtyKeys.begin(); processed++ < kMaxSync && it != dirtyKeys.end(); )\n    {\n        // check ttl\n        int64_t when = QSTORE.TTL(it->first, now);\n\n        if (it->second && when != QStore::ExpireResult::expired)\n        {\n            assert (when != QStore::ExpireResult::notExpire);\n\n            if (when > 0)\n                when += now;\n\n            backends_[dbno]->Put(it->first, *it->second, when);\n            DBG << \"UPDATE leveldb key \" << it->first << \", when = \" << when;\n        }\n        else\n        {\n            backends_[dbno]->Delete(it->first);\n            DBG << \"DELETE leveldb key \" << it->first;\n        }\n            \n        it = dirtyKeys.erase(it);\n    }\n}\n   \nvoid QStore::AddDirtyKey(const QString& key)\n{\n    // put this key to sync list\n    if (!waitSyncKeys_.empty())\n    {\n        QObject* obj = nullptr;\n        GetValue(key, obj);\n        waitSyncKeys_[dbno_][key] = obj;\n    }\n}\n    \nvoid QStore::AddDirtyKey(const QString& key, const QObject* value)\n{\n    // put this key to sync list\n    if (!waitSyncKeys_.empty())\n        waitSyncKeys_[dbno_][key] = value;\n}\n\nstd::vector<QString>  g_dirtyKeys;\n\nvoid Propogate(const std::vector<QString>& params)\n{\n    assert (!params.empty());\n\n    if (!g_dirtyKeys.empty())\n    {\n        for (const auto& k : g_dirtyKeys)\n        {\n            ++ QStore::dirty_;\n            QMulti::Instance().NotifyDirty(QSTORE.GetDB(), k);\n            \n            QSTORE.AddDirtyKey(k); // TODO optimize\n        }\n        g_dirtyKeys.clear();\n    }\n    else if (params.size() > 1)\n    {\n        ++ QStore::dirty_;\n        QMulti::Instance().NotifyDirty(QSTORE.GetDB(), params[1]);\n        QSTORE.AddDirtyKey(params[1]); // TODO optimize\n    }\n\n    if (g_config.appendonly)\n        QAOFThreadController::Instance().SaveCommand(params, QSTORE.GetDB());\n\n    QREPL.SendToSlaves(params);\n}\n\nvoid Propogate(int dbno, const std::vector<QString>& params)\n{\n    QMulti::Instance().NotifyDirtyAll(dbno);\n    Propogate(params);\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QStore.h",
    "content": "#ifndef BERT_QSTORE_H\n#define BERT_QSTORE_H\n\n#include \"QCommon.h\"\n#include \"QSet.h\"\n#include \"QSortedSet.h\"\n#include \"QHash.h\"\n#include \"QList.h\"\n#include \"Timer.h\"\n#include \"QDumpInterface.h\"\n\n#include <vector>\n#include <map>\n#include <memory>\n\nnamespace qedis\n{\n\nusing PSTRING = QString*;\nusing PLIST = QList*;\nusing PSET = QSet*;\nusing PSSET = QSortedSet*;\nusing PHASH = QHash*;\n\n    \nstatic const int kLRUBits = 24;\nstatic const uint32_t kMaxLRUValue = (1 << kLRUBits) - 1;\n\nuint32_t EstimateIdleTime(uint32_t lru);\n\nstruct QObject\n{\npublic:\n    static uint32_t lruclock;\n\n    unsigned int type : 4;\n    unsigned int encoding : 4;\n    unsigned int lru : kLRUBits;\n\n    void* value;\n    \n    explicit\n    QObject(QType = QType_invalid);\n    ~QObject();\n\n    QObject(const QObject& obj) = delete;\n    QObject& operator= (const QObject& obj) = delete;\n    \n    QObject(QObject&& obj);\n    QObject& operator= (QObject&& obj);\n    \n    void Clear();\n    void Reset(void* newvalue = nullptr);\n    \n    static QObject CreateString(const QString& value);\n    static QObject CreateString(long value);\n    static QObject CreateList();\n    static QObject CreateSet();\n    static QObject CreateSSet();\n    static QObject CreateHash();\n    \n    PSTRING  CastString()       const { return reinterpret_cast<PSTRING>(value); }\n    PLIST    CastList()         const { return reinterpret_cast<PLIST>(value);   }\n    PSET     CastSet()          const { return reinterpret_cast<PSET>(value);    }\n    PSSET    CastSortedSet()    const { return reinterpret_cast<PSSET>(value); }\n    PHASH    CastHash()         const { return reinterpret_cast<PHASH>(value);   }\n   \nprivate:\n    void _MoveFrom(QObject&& obj);\n    void _FreeValue();\n};\n\nclass QClient;\n\nusing QDB = std::unordered_map<QString, QObject,\n                               my_hash,\n                               std::equal_to<QString> >;\n\n\nconst int kMaxDbNum = 65536;\n\nclass QStore\n{\npublic:\n    static QStore& Instance();\n    \n    QStore(const QStore& ) = delete;\n    void operator= (const QStore& ) = delete;\n    \n    void Init(int dbNum = 16);\n\n    int SelectDB(int dbno);\n    int GetDB() const;\n    \n    // Key operation\n    bool DeleteKey(const QString& key);\n    bool ExistsKey(const QString& key) const;\n    QType  KeyType(const QString& key) const;\n    QString RandomKey(QObject** val = nullptr) const;\n    size_t DBSize() const { return store_[dbno_].size(); }\n    size_t ScanKey(size_t cursor, size_t count, std::vector<QString>& res) const;\n\n    // iterator\n    QDB::const_iterator begin() const   { return store_[dbno_].begin(); }\n    QDB::const_iterator end()   const   { return store_[dbno_].end(); }\n    QDB::iterator       begin()         { return store_[dbno_].begin(); }\n    QDB::iterator       end()           { return store_[dbno_].end(); }\n    \n    const QObject* GetObject(const QString& key) const;\n    QError GetValue(const QString& key, QObject*& value, bool touch = true);\n    QError GetValueByType(const QString& key, QObject*& value, QType type = QType_invalid);\n    // do not update lru time\n    QError  GetValueByTypeNoTouch(const QString& key, QObject*& value, QType type = QType_invalid);\n\n    QObject* SetValue(const QString& key, QObject&& value);\n\n    // for expire key\n    enum ExpireResult : std::int8_t\n    {\n        notExpire=  0,\n        persist  = -1,\n        expired  = -2,\n        notExist = -2,\n    };\n    void    SetExpire(const QString& key, uint64_t when) const;\n    void    SetExpireAfter(const QString& key, uint64_t ttl) const;\n    int64_t TTL(const QString& key, uint64_t now);\n    bool    ClearExpire(const QString& key);\n    int     LoopCheckExpire(uint64_t now);\n    void    InitExpireTimer();\n    \n    // danger cmd\n    void    ClearCurrentDB() { store_[dbno_].clear(); }\n    void    ResetDb();\n    \n    // for blocked list\n    bool    BlockClient(const QString& key,\n                        QClient* client,\n                        uint64_t timeout,\n                        ListPosition pos,\n                        const QString* dstList = 0);\n    size_t  UnblockClient(QClient* client);\n    size_t  ServeClient(const QString& key, const PLIST& list);\n    \n    int     LoopCheckBlocked(uint64_t now);\n    void    InitBlockedTimer();\n    \n    size_t  BlockedSize() const;\n    \n    static  int dirty_;\n\n    // eviction timer for lru\n    void    InitEvictionTimer();\n    // for backends\n    void    InitDumpBackends();\n    void    DumpToBackends(int dbno);\n    void    AddDirtyKey(const QString& key);\n    void    AddDirtyKey(const QString& key, const QObject* value);\n    \nprivate:\n    QStore() : dbno_(0)\n    {\n    }\n    \n    QError  _GetValueByType(const QString& key, QObject*& value, QType type = QType_invalid, bool touch = true);\n\n    ExpireResult    _ExpireIfNeed(const QString& key, uint64_t now);\n    \n    class ExpiresDB\n    {\n    public:\n        void SetExpire(const QString& key, uint64_t when);\n        int64_t TTL(const QString& key, uint64_t now);\n        bool ClearExpire(const QString& key);\n        ExpireResult ExpireIfNeed(const QString& key, uint64_t now);\n\n        int LoopCheck(uint64_t now);\n        \n    private:\n        using Q_EXPIRE_DB = std::unordered_map<QString, uint64_t,\n                                    my_hash,\n                                    std::equal_to<QString> >;\n        Q_EXPIRE_DB expireKeys_;  // all the keys to be expired, unorder.\n    };\n    \n    class BlockedClients\n    {\n    public:\n        bool BlockClient(const QString& key,\n                            QClient* client,\n                            uint64_t timeout,\n                            ListPosition  pos,\n                            const QString* dstList = 0);\n        size_t UnblockClient(QClient* client);\n        size_t ServeClient(const QString& key, const PLIST& list);\n        \n        int LoopCheck(uint64_t now);\n        size_t Size() const { return blockedClients_.size(); }\n    private:\n        using Clients = std::list<std::tuple<std::weak_ptr<QClient>, uint64_t, ListPosition> >;\n        using WaitingList = std::unordered_map<QString, Clients>;\n        \n        WaitingList blockedClients_;\n    };\n\n    QError _SetValue(const QString& key, QObject& value, bool exclusive = false);\n\n    // Because GetObject() must be const, so mutable them\n    mutable std::vector<QDB> store_;\n    mutable std::vector<ExpiresDB> expiresDb_;\n    std::vector<BlockedClients> blockedClients_;\n    std::vector<std::unique_ptr<QDumpInterface> > backends_;\n        \n    using ToSyncDb = std::unordered_map<QString, const QObject* ,\n                                        my_hash,\n                                        std::equal_to<QString> >;\n    std::vector<ToSyncDb> waitSyncKeys_;\n    int dbno_;\n};\n\n#define QSTORE  QStore::Instance()\n\n// ugly, but I don't want to write signalModifiedKey() every where\nextern std::vector<QString> g_dirtyKeys;\nextern void Propogate(const std::vector<QString>& params);\nextern void Propogate(int dbno, const std::vector<QString>& params);\n    \n}\n\n#endif\n\n"
  },
  {
    "path": "QedisCore/QString.cc",
    "content": "#include \"QString.h\"\n#include \"QStore.h\"\n#include \"Log/Logger.h\"\n#include <cassert>\n\nnamespace qedis\n{\n\nQObject QObject::CreateString(const QString& value)\n{\n    QObject obj(QType_string);\n\n    long val;\n    if (Strtol(value.c_str(), value.size(), &val))\n    {\n        obj.encoding = QEncode_int;\n        obj.value = (void*)val;\n        DBG << \"set long value \" << val;\n    }\n    else\n    {\n        obj.encoding = QEncode_raw;\n        obj.value = new QString(value);\n    }\n\n    return obj;\n}\n\nQObject QObject::CreateString(long val)\n{\n    QObject obj(QType_string);\n    \n    obj.encoding = QEncode_int;\n    obj.value = (void*)val;\n    \n    return obj;\n}\n\n    \nstatic void DeleteString(QString* s)\n{\n    delete s;\n}\n\nstatic void NotDeleteString(QString* )\n{\n}\n\nstd::unique_ptr<QString, void (*)(QString* )>\n    GetDecodedString(const QObject* value)\n{\n    if (value->encoding == QEncode_raw)\n    {\n        return std::unique_ptr<QString, void (*)(QString* )>(value->CastString(), NotDeleteString);\n    }\n    else if (value->encoding == QEncode_int)\n    {\n        intptr_t val = (intptr_t)value->value;\n        \n        char vbuf[32];\n        snprintf(vbuf, sizeof vbuf - 1, \"%ld\",  val);\n        return std::unique_ptr<QString, void (*)(QString* )>(new QString(vbuf), DeleteString);\n    }\n    else\n    {\n        assert (!!!\"error string encoding\");\n    }\n        \n    return std::unique_ptr<QString, void (*)(QString* )>(nullptr, NotDeleteString);\n}\n\nstatic bool SetValue(const QString& key, const QString& value, bool exclusive = false)\n{\n    if (exclusive)\n    {\n        QObject* val;\n        if (QSTORE.GetValue(key, val) == QError_ok)\n            return false;\n    }\n\n    QSTORE.ClearExpire(key); // clear key's old ttl\n    QSTORE.SetValue(key, QObject::CreateString(value));\n\n    return true;\n}\n\nQError set(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    SetValue(params[1], params[2]);\n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError setnx(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (SetValue(params[1], params[2], true))\n        Format1(reply);\n    else\n        Format0(reply);\n\n    return QError_ok;\n}\n\nQError mset(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 != 1)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n\n    for (size_t i = 1; i < params.size(); i += 2)\n    {\n        g_dirtyKeys.push_back(params[i]);\n        SetValue(params[i], params[i + 1]);\n    }\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError msetnx(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    if (params.size() % 2 != 1)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n\n    for (size_t i = 1; i < params.size(); i += 2)\n    {\n        QObject* val;\n        if (QSTORE.GetValue(params[i], val) == QError_ok)\n        {\n            Format0(reply);\n            return QError_ok;\n        }\n    }\n\n    for (size_t i = 1; i < params.size(); i += 2)\n    {\n        g_dirtyKeys.push_back(params[i]);\n        SetValue(params[i], params[i + 1]);\n    }\n\n    Format1(reply);\n    return QError_ok;\n}\n\nQError setex(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    long seconds;\n    if (!Strtol(params[2].c_str(), params[2].size(), &seconds))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    const auto& key = params[1];\n    QSTORE.SetValue(key, QObject::CreateString(params[3]));\n    QSTORE.SetExpire(key, ::Now() + seconds * 1000);\n\n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError psetex(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    long milliseconds;\n    if (!Strtol(params[2].c_str(), params[2].size(), &milliseconds))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    const auto& key = params[1];\n    QSTORE.SetValue(key, QObject::CreateString(params[3]));\n    QSTORE.SetExpire(key, ::Now() + milliseconds);\n    \n    FormatOK(reply);\n    return QError_ok;\n}\n\nQError setrange(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    long offset;\n    if (!Strtol(params[2].c_str(), params[2].size(), &offset))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err != QError_ok)\n    {\n        if (err == QError_notExist)\n        {\n            value = QSTORE.SetValue(params[1], QObject::CreateString(\"\"));\n        }\n        else\n        {\n            ReplyError(err, reply); \n            return err;  \n        }\n    }\n\n    auto str = GetDecodedString(value);\n    const size_t newSize = offset + params[3].size();\n\n    if (newSize > str->size())  str->resize(newSize, '\\0');\n    str->replace(offset, params[3].size(), params[3]);\n    \n    if (value->encoding == QEncode_int)\n    {\n        value->Reset(new QString(*str));\n        value->encoding = QEncode_raw;\n    }\n\n    FormatInt(static_cast<long>(str->size()), reply);\n    return QError_ok;\n}\n\n\nstatic void AddReply(QObject* value, UnboundedBuffer* reply)\n{\n    auto str = GetDecodedString(value);\n    FormatBulk(str->c_str(), str->size(), reply);\n}\n\nQError get(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err != QError_ok) \n    {\n        if (err == QError_notExist)\n            FormatNull(reply); \n        else\n            ReplyError(err, reply);\n\n        return err;  \n    }\n\n    AddReply(value, reply);\n    return QError_ok;\n}\n\nQError mget(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    PreFormatMultiBulk(params.size() - 1, reply);\n    for (size_t i = 1; i < params.size(); ++ i)\n    {\n        QObject* value;\n        QError err = QSTORE.GetValueByType(params[i], value, QType_string);\n        if (err != QError_ok)\n            FormatNull(reply);\n        else\n            AddReply(value, reply);\n    }\n    \n    return QError_ok;\n}\n\nQError getrange(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err != QError_ok)\n    {\n        if (err == QError_notExist)\n            FormatBulk(\"\", 0, reply);\n        else\n            ReplyError(err, reply);\n\n        return err;  \n    }\n\n    long start = 0, end = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &start) ||\n        !Strtol(params[3].c_str(), params[3].size(), &end))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    auto str = GetDecodedString(value);\n    AdjustIndex(start, end, str->size());\n\n    if (start <= end)\n        FormatBulk(&(*str)[start], end - start + 1, reply);\n    else\n        FormatEmptyBulk(reply);\n\n    return QError_ok;\n}\n\nQError  getset(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value = nullptr;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n\n    switch (err)\n    {\n    case QError_notExist:\n        // fall through\n\n    case QError_ok:\n        if (!value)\n            FormatNull(reply);\n        else    \n            FormatBulk(*GetDecodedString(value), reply);\n\n        QSTORE.SetValue(params[1], QObject::CreateString(params[2]));\n        break;\n\n    default:\n        ReplyError(err, reply); \n        return err;  \n    }\n\n    return QError_ok;\n}\n\nQError  append(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n\n    switch (err)\n    {\n    case QError_ok:\n        {\n            auto s = GetDecodedString(value);\n            value = QSTORE.SetValue(params[1], QObject::CreateString(*s + params[2]));\n        }\n        break;\n\n    case QError_notExist:\n        value = QSTORE.SetValue(params[1], QObject::CreateString(params[2]));\n        break;\n\n    default:\n        ReplyError(err, reply);\n        return err;\n    };\n\n    auto s = GetDecodedString(value);\n    FormatInt(static_cast<long>(s->size()), reply);\n    return QError_ok;\n}\n\nQError bitcount(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err != QError_ok)\n    {\n        if (err == QError_type)\n            ReplyError(QError_type, reply);\n        else\n            Format0(reply);\n\n        return QError_ok;\n    }\n\n    if (params.size() != 2 && params.size() != 4)\n    {\n        ReplyError(QError_param, reply);\n        return QError_param;\n    }\n\n    long start = 0;\n    long end   = -1;\n    if (params.size() == 4)\n    {\n        if (!Strtol(params[2].c_str(), params[2].size(), &start) ||\n            !Strtol(params[3].c_str(), params[3].size(), &end))\n        {\n            ReplyError(QError_nan, reply);\n            return QError_nan;\n        }\n    }\n\n    auto str = GetDecodedString(value);\n    AdjustIndex(start, end, str->size());\n\n    size_t cnt = 0;\n    if (end >= start)\n    {\n        cnt = BitCount((const uint8_t*)str->data() + start,  end - start + 1);\n    }\n\n    FormatInt(static_cast<long>(cnt), reply);\n    return QError_ok;\n}\n\nQError getbit(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return  QError_ok;\n    }\n\n    long offset = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &offset))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n    \n    auto str = GetDecodedString(value);\n    const uint8_t* buf = (const uint8_t*)str->c_str();\n    size_t  size = 8 * str->size();\n\n    if (offset < 0 || offset >= static_cast<long>(size))\n    {\n        Format0(reply);\n        return QError_ok;\n    }\n\n    size_t  bytesOffset = offset / 8;\n    size_t  bitsOffset  = offset % 8;\n    uint8_t byte = buf[bytesOffset];\n    if (byte & (0x1 << bitsOffset))\n        Format1(reply);\n    else\n        Format0(reply);\n\n    return QError_ok;\n}\n\nQError setbit(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(params[1], value, QType_string);\n    if (err == QError_notExist)\n    {\n        value = QSTORE.SetValue(params[1], QObject::CreateString(\"\"));\n        err = QError_ok;\n    }\n\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return err;\n    }\n\n    long offset = 0;\n    long on     = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &offset) || \n        !Strtol(params[3].c_str(), params[3].size(), &on))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    if (offset < 0 || offset > kStringMaxBytes)\n    {\n        Format0(reply);\n        return QError_ok;\n    }\n\n    QString newVal(*GetDecodedString(value));\n\n    size_t  bytes = offset / 8;\n    size_t  bits  = offset % 8;\n\n    if (bytes + 1 > newVal.size())     newVal.resize(bytes + 1, '\\0');\n\n    const char oldByte = newVal[bytes];\n    char& byte = newVal[bytes];\n    if (on)\n        byte |= (0x1 << bits);\n    else\n        byte &= ~(0x1 << bits);\n\n    value->Reset(new QString(newVal));\n    value->encoding = QEncode_raw;\n    FormatInt((oldByte & (0x1 << bits)) ? 1 : 0, reply);\n\n    return QError_ok;\n}\n\nstatic QError ChangeFloatValue(const QString& key, float delta, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(key, value, QType_string);\n    if (err == QError_notExist)\n    {\n        value = QSTORE.SetValue(key, QObject::CreateString(\"0\"));\n        err = QError_ok;\n    }\n\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return  err;\n    }\n\n    auto val = GetDecodedString(value);\n    float oldVal = 0;\n    if (!Strtof(val->c_str(), val->size(), &oldVal))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    char newVal[32];\n    int len = snprintf(newVal, sizeof newVal - 1, \"%.6g\", (oldVal + delta));\n    value->Reset(new QString(newVal, len));\n    value->encoding = QEncode_raw;\n\n    FormatBulk(newVal, len, reply);\n    return QError_ok;\n}\n\nQError incrbyfloat(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    float delta = 0;\n    if (!Strtof(params[2].c_str(), params[2].size(), &delta))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    return ChangeFloatValue(params[1], delta, reply);\n}\n\nstatic QError ChangeIntValue(const QString& key, long delta, UnboundedBuffer* reply)\n{\n    QObject* value;\n    QError err = QSTORE.GetValueByType(key, value, QType_string);\n    if (err == QError_notExist)\n    {\n        value = QSTORE.SetValue(key, QObject::CreateString(0));\n        err = QError_ok;\n    }\n\n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n        return  err;\n    }\n\n    if (value->encoding != QEncode_int)\n    {\n        ReplyError(QError_nan, reply);\n        return QError_ok;\n    }\n\n    intptr_t oldVal = (intptr_t)value->value;\n    value->Reset((void*)(oldVal + delta));\n\n    FormatInt(oldVal + delta, reply);\n    return QError_ok;\n}\n\nQError incr(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return ChangeIntValue(params[1], 1, reply);\n}\nQError decr(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    return ChangeIntValue(params[1], -1, reply);\n}\n\nQError incrby(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    long delta = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &delta))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    return ChangeIntValue(params[1], delta, reply);\n}\n\nQError decrby(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    long delta = 0;\n    if (!Strtol(params[2].c_str(), params[2].size(), &delta))\n    {\n        ReplyError(QError_nan, reply);\n        return QError_nan;\n    }\n\n    return ChangeIntValue(params[1], -delta, reply);\n}\n\nQError strlen(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    QObject* val;\n    QError   err = QSTORE.GetValueByType(params[1], val, QType_string);\n    if (err != QError_ok)\n    {\n        Format0(reply);\n        return err;\n    }\n    \n    auto str = GetDecodedString(val);\n    FormatInt(static_cast<long>(str->size()), reply);\n    return QError_ok;\n}\n\nenum BitOp\n{\n    BitOp_and,\n    BitOp_or,\n    BitOp_not,\n    BitOp_xor,\n};\n\nstatic QString StringBitOp(const std::vector<const QString* >& keys, BitOp op)\n{\n    QString res;\n    \n    switch (op)\n    {\n        case BitOp_and:\n        case BitOp_or:\n        case BitOp_xor:\n            for (auto k : keys)\n            {\n                QObject* val;\n                if (QSTORE.GetValueByType(*k, val, QType_string) != QError_ok)\n                    continue;\n                \n                auto str = GetDecodedString(val);\n                if (res.empty())\n                {\n                    res = *str;\n                    continue;\n                }\n                \n                if (str->size() > res.size())\n                    res.resize(str->size());\n\n                for (size_t i = 0; i < str->size(); ++ i)\n                {\n                    if (op == BitOp_and)\n                        res[i] &= (*str)[i];\n                    else if (op == BitOp_or)\n                        res[i] |= (*str)[i];\n                    else if (op == BitOp_xor)\n                        res[i] ^= (*str)[i];\n                }\n            }\n            break;\n            \n        case BitOp_not:\n        {\n            assert(keys.size() == 1);\n            QObject* val;\n            if (QSTORE.GetValueByType(*keys[0], val, QType_string) != QError_ok)\n                break;\n            \n            auto str = GetDecodedString(val);\n            res.resize(str->size());\n\n            for (size_t i = 0; i < str->size(); ++ i)\n            {\n                res[i] = ~(*str)[i];\n            }\n    \n            break;\n        }\n\n        default:\n            break;\n    }\n    \n    return res;\n}\n\n\nQError  bitop(const std::vector<QString>& params, UnboundedBuffer* reply)\n{\n    std::vector<const QString* > keys;\n    for (size_t i = 3; i < params.size(); ++ i)\n        keys.push_back(&params[i]);\n    \n    QError err = QError_param;\n    QString res;\n    if (params[1].size() == 2)\n    {\n        if (strncasecmp(params[1].c_str(), \"or\", 2) == 0)\n        {\n            err = QError_ok;\n            res = StringBitOp(keys, BitOp_or);\n        }\n    }\n    else if (params[1].size() == 3)\n    {\n        if (strncasecmp(params[1].c_str(), \"xor\", 3) == 0)\n        {\n            err = QError_ok;\n            res = StringBitOp(keys, BitOp_xor);\n        }\n        else if (strncasecmp(params[1].c_str(), \"and\", 3) == 0)\n        {\n            err = QError_ok;\n            res = StringBitOp(keys, BitOp_and);\n        }\n        else if (strncasecmp(params[1].c_str(), \"not\", 3) == 0)\n        {\n            if (params.size() == 4)\n            {\n                err = QError_ok;\n                res = StringBitOp(keys, BitOp_not);\n            }\n        }\n        else\n        {\n            ;\n        }\n    }\n    \n    if (err != QError_ok)\n    {\n        ReplyError(err, reply);\n    }\n    else\n    {\n        QSTORE.SetValue(params[2], QObject::CreateString(res));\n        FormatInt(static_cast<long>(res.size()), reply);\n    }\n\n    return QError_ok;\n}\n\n}\n"
  },
  {
    "path": "QedisCore/QString.h",
    "content": "#ifndef BERT_QSTRING_H\n#define BERT_QSTRING_H\n\n#include <string>\n#include <memory>\n\nnamespace qedis\n{\n\nusing QString = std::string;\n\n//typedef std::basic_string<char, std::char_traits<char>, Bert::Allocator<char> >  QString;\n\nstruct QObject;\n\nstd::unique_ptr<QString, void (*)(QString* )>\nGetDecodedString(const QObject* value);\n\n}\n\n#endif\n"
  },
  {
    "path": "QedisCore/crc64.c",
    "content": "/* Redis uses the CRC64 variant with \"Jones\" coefficients and init value of 0.\n *\n * Specification of this CRC64 variant follows:\n * Name: crc-64-jones\n * Width: 64 bites\n * Poly: 0xad93d23594c935a9\n * Reflected In: True\n * Xor_In: 0xffffffffffffffff\n * Reflected_Out: True\n * Xor_Out: 0x0\n * Check(\"123456789\"): 0xe9c6d914c4b8d9ca\n *\n * Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE. */\n\n#include <stdint.h>\n\nstatic const uint64_t crc64_tab[256] = {\n    UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),\n    UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),\n    UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),\n    UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),\n    UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),\n    UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),\n    UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),\n    UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),\n    UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),\n    UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),\n    UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),\n    UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),\n    UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),\n    UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),\n    UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),\n    UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),\n    UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),\n    UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),\n    UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),\n    UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),\n    UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),\n    UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),\n    UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),\n    UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),\n    UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),\n    UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),\n    UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),\n    UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),\n    UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),\n    UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),\n    UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),\n    UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),\n    UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),\n    UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),\n    UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),\n    UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),\n    UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),\n    UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),\n    UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),\n    UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),\n    UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),\n    UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),\n    UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),\n    UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),\n    UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),\n    UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),\n    UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),\n    UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),\n    UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),\n    UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),\n    UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),\n    UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),\n    UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),\n    UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),\n    UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),\n    UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),\n    UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),\n    UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),\n    UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),\n    UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),\n    UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),\n    UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),\n    UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),\n    UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),\n    UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),\n    UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),\n    UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),\n    UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),\n    UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),\n    UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),\n    UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),\n    UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),\n    UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),\n    UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),\n    UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),\n    UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),\n    UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),\n    UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),\n    UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),\n    UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),\n    UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),\n    UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),\n    UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),\n    UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),\n    UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),\n    UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),\n    UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),\n    UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),\n    UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),\n    UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),\n    UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),\n    UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),\n    UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),\n    UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),\n    UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),\n    UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),\n    UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),\n    UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),\n    UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),\n    UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),\n    UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),\n    UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),\n    UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),\n    UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),\n    UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),\n    UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),\n    UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),\n    UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),\n    UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),\n    UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),\n    UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),\n    UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),\n    UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),\n    UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),\n    UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),\n    UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),\n    UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),\n    UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),\n    UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),\n    UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),\n    UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),\n    UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),\n    UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),\n    UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),\n    UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),\n    UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),\n    UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),\n    UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),\n};\n\nuint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {\n    uint64_t j;\n\n    for (j = 0; j < l; j++) {\n        uint8_t byte = s[j];\n        crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);\n    }\n    return crc;\n}\n\n/* Test main */\n#ifdef TEST_MAIN\n#include <stdio.h>\nint main(void) {\n    printf(\"e9c6d914c4b8d9ca == %016llx\\n\",\n        (unsigned long long) crc64(0,(unsigned char*)\"123456789\",9));\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "QedisCore/redisIntset.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 \"redisIntset.h\"\n\n#define memrev16ifbe(x)   (x)\n#define memrev32ifbe(x)   (x)\n#define memrev64ifbe(x)   (x)\n#define intrev16ifbe(x)   (x)\n#define intrev32ifbe(x)   (x)\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] = (int32_t)value;\n        //memrev32ifbe(((int32_t*)is->contents)+pos);\n    } else {\n        ((int16_t*)is->contents)[pos] = (int16_t)value;\n        //memrev16ifbe(((int16_t*)is->contents)+pos);\n    }\n}\n\n/* Create an empty intset. */\nintset *intsetNew(void) {\n    intset *is = malloc(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 = realloc(is,sizeof(intset)+size);\n    return is;\n}\n\n/* Search for the position of \"value\". Return 1 when the value was found and\n * sets \"pos\" to the position of the value within the intset. Return 0 when\n * the value is not present in the intset and sets \"pos\" to the position\n * where \"value\" can be inserted. */\nstatic uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {\n    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;\n    int64_t cur = -1;\n\n    /* The value can never be found when the set is empty */\n    if (intrev32ifbe(is->length) == 0) {\n        if (pos) *pos = 0;\n        return 0;\n    } else {\n        /* Check for the case where we know we cannot find the value,\n         * but do know the insert position. */\n        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {\n            if (pos) *pos = intrev32ifbe(is->length);\n            return 0;\n        } else if (value < _intsetGet(is,0)) {\n            if (pos) *pos = 0;\n            return 0;\n        }\n    }\n\n    while(max >= min) {\n        mid = (min+max)/2;\n        cur = _intsetGet(is,mid);\n        if (value > cur) {\n            min = mid+1;\n        } else if (value < cur) {\n            max = mid-1;\n        } else {\n            break;\n        }\n    }\n\n    if (value == cur) {\n        if (pos) *pos = mid;\n        return 1;\n    } else {\n        if (pos) *pos = min;\n        return 0;\n    }\n}\n\n/* Upgrades the intset to a larger encoding and inserts the given integer. */\nstatic intset *intsetUpgradeAndAdd(intset *is, int64_t value) {\n    uint8_t curenc = intrev32ifbe(is->encoding);\n    uint8_t newenc = _intsetValueEncoding(value);\n    int length = intrev32ifbe(is->length);\n    int prepend = value < 0 ? 1 : 0;\n\n    /* First set new encoding and resize */\n    is->encoding = intrev32ifbe(newenc);\n    is = intsetResize(is,intrev32ifbe(is->length)+1);\n\n    /* Upgrade back-to-front so we don't overwrite values.\n     * Note that the \"prepend\" variable is used to make sure we have an empty\n     * space at either the beginning or the end of the intset. */\n    while(length--)\n        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));\n\n    /* Set the value at the beginning or the end. */\n    if (prepend)\n        _intsetSet(is,0,value);\n    else\n        _intsetSet(is,intrev32ifbe(is->length),value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\nstatic void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {\n    void *src, *dst;\n    uint32_t bytes = intrev32ifbe(is->length)-from;\n    uint32_t encoding = intrev32ifbe(is->encoding);\n\n    if (encoding == INTSET_ENC_INT64) {\n        src = (int64_t*)is->contents+from;\n        dst = (int64_t*)is->contents+to;\n        bytes *= sizeof(int64_t);\n    } else if (encoding == INTSET_ENC_INT32) {\n        src = (int32_t*)is->contents+from;\n        dst = (int32_t*)is->contents+to;\n        bytes *= sizeof(int32_t);\n    } else {\n        src = (int16_t*)is->contents+from;\n        dst = (int16_t*)is->contents+to;\n        bytes *= sizeof(int16_t);\n    }\n    memmove(dst,src,bytes);\n}\n\n/* Insert an integer in the intset */\nintset *intsetAdd(intset *is, int64_t value, uint8_t *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 1;\n\n    /* Upgrade encoding if necessary. If we need to upgrade, we know that\n     * this value should be either appended (if > 0) or prepended (if < 0),\n     * because it lies outside the range of existing values. */\n    if (valenc > intrev32ifbe(is->encoding)) {\n        /* This always succeeds, so we don't need to curry *success. */\n        return intsetUpgradeAndAdd(is,value);\n    } else {\n        /* Abort if the value is already present in the set.\n         * This call will populate \"pos\" with the right position to insert\n         * the value when it cannot be found. */\n        if (intsetSearch(is,value,&pos)) {\n            if (success) *success = 0;\n            return is;\n        }\n\n        is = intsetResize(is,intrev32ifbe(is->length)+1);\n        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);\n    }\n\n    _intsetSet(is,pos,value);\n    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);\n    return is;\n}\n\n/* Delete integer from intset */\nintset *intsetRemove(intset *is, int64_t value, int *success) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    uint32_t pos;\n    if (success) *success = 0;\n\n    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {\n        uint32_t len = intrev32ifbe(is->length);\n\n        /* We know we can delete */\n        if (success) *success = 1;\n\n        /* Overwrite value with tail and update length */\n        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);\n        is = intsetResize(is,len-1);\n        is->length = intrev32ifbe(len-1);\n    }\n    return is;\n}\n\n/* Determine whether a value belongs to this set */\nuint8_t intsetFind(intset *is, int64_t value) {\n    uint8_t valenc = _intsetValueEncoding(value);\n    return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);\n}\n\n/* Return random member */\nint64_t intsetRandom(intset *is) {\n    return _intsetGet(is,rand()%intrev32ifbe(is->length));\n}\n\n/* Sets the value to the value at the given position. When this position is\n * out of range the function returns 0, when in range it returns 1. */\nuint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {\n    if (pos < intrev32ifbe(is->length)) {\n        *value = _intsetGet(is,pos);\n        return 1;\n    }\n    return 0;\n}\n\n/* Return intset length */\nuint32_t intsetLen(intset *is) {\n    return intrev32ifbe(is->length);\n}\n\n/* Return intset blob size in bytes. */\nsize_t intsetBlobLen(intset *is) {\n    return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);\n}\n\n#ifdef INTSET_TEST_MAIN\n#include <sys/time.h>\n\nvoid intsetRepr(intset *is) {\n    int i;\n    for (i = 0; i < intrev32ifbe(is->length); i++) {\n        printf(\"%lld\\n\", (uint64_t)_intsetGet(is,i));\n    }\n    printf(\"\\n\");\n}\n\nvoid error(char *err) {\n    printf(\"%s\\n\", err);\n    exit(1);\n}\n\nvoid ok(void) {\n    printf(\"OK\\n\");\n}\n\nlong long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\n#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))\nvoid _assert(char *estr, char *file, int line) {\n    printf(\"\\n\\n=== ASSERTION FAILED ===\\n\");\n    printf(\"==> %s:%d '%s' is not true\\n\",file,line,estr);\n}\n\nintset *createSet(int bits, int size) {\n    uint64_t mask = (1<<bits)-1;\n    uint64_t i, value;\n    intset *is = intsetNew();\n\n    for (i = 0; i < size; i++) {\n        if (bits > 32) {\n            value = (rand()*rand()) & mask;\n        } else {\n            value = rand() & mask;\n        }\n        is = intsetAdd(is,value,NULL);\n    }\n    return is;\n}\n\nvoid checkConsistency(intset *is) {\n    int i;\n\n    for (i = 0; i < (intrev32ifbe(is->length)-1); i++) {\n        uint32_t encoding = intrev32ifbe(is->encoding);\n\n        if (encoding == INTSET_ENC_INT16) {\n            int16_t *i16 = (int16_t*)is->contents;\n            assert(i16[i] < i16[i+1]);\n        } else if (encoding == INTSET_ENC_INT32) {\n            int32_t *i32 = (int32_t*)is->contents;\n            assert(i32[i] < i32[i+1]);\n        } else {\n            int64_t *i64 = (int64_t*)is->contents;\n            assert(i64[i] < i64[i+1]);\n        }\n    }\n}\n\nint main(int argc, char **argv) {\n    uint8_t success;\n    int i;\n    intset *is;\n    sranddev();\n\n    printf(\"Value encodings: \"); {\n        assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16);\n        assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16);\n        assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32);\n        assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64);\n        assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64);\n        ok();\n    }\n\n    printf(\"Basic adding: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,5,&success); assert(success);\n        is = intsetAdd(is,6,&success); assert(success);\n        is = intsetAdd(is,4,&success); assert(success);\n        is = intsetAdd(is,4,&success); assert(!success);\n        ok();\n    }\n\n    printf(\"Large number of random adds: \"); {\n        int inserts = 0;\n        is = intsetNew();\n        for (i = 0; i < 1024; i++) {\n            is = intsetAdd(is,rand()%0x800,&success);\n            if (success) inserts++;\n        }\n        assert(intrev32ifbe(is->length) == inserts);\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int16 to int32: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,65535));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,-65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,-65535));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int16 to int64: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,4294967295));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,32,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);\n        is = intsetAdd(is,-4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,32));\n        assert(intsetFind(is,-4294967295));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Upgrade from int32 to int64: \"); {\n        is = intsetNew();\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        is = intsetAdd(is,4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,65535));\n        assert(intsetFind(is,4294967295));\n        checkConsistency(is);\n\n        is = intsetNew();\n        is = intsetAdd(is,65535,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);\n        is = intsetAdd(is,-4294967295,NULL);\n        assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);\n        assert(intsetFind(is,65535));\n        assert(intsetFind(is,-4294967295));\n        checkConsistency(is);\n        ok();\n    }\n\n    printf(\"Stress lookups: \"); {\n        long num = 100000, size = 10000;\n        int i, bits = 20;\n        long long start;\n        is = createSet(bits,size);\n        checkConsistency(is);\n\n        start = usec();\n        for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL);\n        printf(\"%ld lookups, %ld element set, %lldusec\\n\",num,size,usec()-start);\n    }\n\n    printf(\"Stress add+delete: \"); {\n        int i, v1, v2;\n        is = intsetNew();\n        for (i = 0; i < 0xffff; i++) {\n            v1 = rand() % 0xfff;\n            is = intsetAdd(is,v1,NULL);\n            assert(intsetFind(is,v1));\n\n            v2 = rand() % 0xfff;\n            is = intsetRemove(is,v2,NULL);\n            assert(!intsetFind(is,v2));\n        }\n        checkConsistency(is);\n        ok();\n    }\n}\n#endif\n"
  },
  {
    "path": "QedisCore/redisIntset.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(intset *is);\nsize_t intsetBlobLen(intset *is);\n\n#endif // __INTSET_H\n"
  },
  {
    "path": "QedisCore/redisZipList.c",
    "content": "/* The ziplist is a specially encoded dually linked list that is designed\n * to be very memory efficient. It stores both strings and integer values,\n * where integers are encoded as actual integers instead of a series of\n * characters. It allows push and pop operations on either side of the list\n * in O(1) time. However, because every operation requires a reallocation of\n * the memory used by the ziplist, the actual complexity is related to the\n * amount of memory used by the ziplist.\n *\n * ----------------------------------------------------------------------------\n *\n * ZIPLIST OVERALL LAYOUT:\n * The general layout of the ziplist is as follows:\n * <zlbytes><zltail><zllen><entry><entry><zlend>\n *\n * <zlbytes> is an unsigned integer to hold the number of bytes that the\n * ziplist occupies. This value needs to be stored to be able to resize the\n * entire structure without the need to traverse it first.\n *\n * <zltail> is the offset to the last entry in the list. This allows a pop\n * operation on the far side of the list without the need for full traversal.\n *\n * <zllen> is the number of entries.When this value is larger than 2**16-2,\n * we need to traverse the entire list to know how many items it holds.\n *\n * <zlend> is a single byte special value, equal to 255, which indicates the\n * end of the list.\n *\n * ZIPLIST ENTRIES:\n * Every entry in the ziplist is prefixed by a header that contains two pieces\n * of information. First, the length of the previous entry is stored to be\n * able to traverse the list from back to front. Second, the encoding with an\n * optional string length of the entry itself is stored.\n *\n * The length of the previous entry is encoded in the following way:\n * If this length is smaller than 254 bytes, it will only consume a single\n * byte that takes the length as value. When the length is greater than or\n * equal to 254, it will consume 5 bytes. The first byte is set to 254 to\n * indicate a larger value is following. The remaining 4 bytes take the\n * length of the previous entry as value.\n *\n * The other header field of the entry itself depends on the contents of the\n * entry. When the entry is a string, the first 2 bits of this header will hold\n * the type of encoding used to store the length of the string, followed by the\n * actual length of the string. When the entry is an integer the first 2 bits\n * are both set to 1. The following 2 bits are used to specify what kind of\n * integer will be stored after this header. An overview of the different\n * types and encodings is as follows:\n *\n * |00pppppp| - 1 byte\n *      String value with length less than or equal to 63 bytes (6 bits).\n * |01pppppp|qqqqqqqq| - 2 bytes\n *      String value with length less than or equal to 16383 bytes (14 bits).\n * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes\n *      String value with length greater than or equal to 16384 bytes.\n * |11000000| - 1 byte\n *      Integer encoded as int16_t (2 bytes).\n * |11010000| - 1 byte\n *      Integer encoded as int32_t (4 bytes).\n * |11100000| - 1 byte\n *      Integer encoded as int64_t (8 bytes).\n * |11110000| - 1 byte\n *      Integer encoded as 24 bit signed (3 bytes).\n * |11111110| - 1 byte\n *      Integer encoded as 8 bit signed (1 byte).\n * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.\n *      Unsigned integer from 0 to 12. The encoded value is actually from\n *      1 to 13 because 0000 and 1111 can not be used, so 1 should be\n *      subtracted from the encoded 4 bit value to obtain the right value.\n * |11111111| - End of ziplist.\n *\n * All the integers are represented in little endian byte order.\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <limits.h>\n#include <assert.h>\n#include \"redisZipList.h\"\n\n#define memrev32ifbe(x)   (x)\n#define intrev32ifbe(x)   (x)\n#define intrev16ifbe(x)   (x)\n\n#define ZIP_END 255\n#define ZIP_BIGLEN 254\n\n/* Different encoding/length possibilities */\n#define ZIP_STR_MASK 0xc0\n#define ZIP_INT_MASK 0x30\n#define ZIP_STR_06B (0 << 6)\n#define ZIP_STR_14B (1 << 6)\n#define ZIP_STR_32B (2 << 6)\n#define ZIP_INT_16B (0xc0 | 0<<4)\n#define ZIP_INT_32B (0xc0 | 1<<4)\n#define ZIP_INT_64B (0xc0 | 2<<4)\n#define ZIP_INT_24B (0xc0 | 3<<4)\n#define ZIP_INT_8B 0xfe\n/* 4 bit integer immediate encoding */\n#define ZIP_INT_IMM_MASK 0x0f\n#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */\n#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */\n#define ZIP_INT_IMM_VAL(v) (v & ZIP_INT_IMM_MASK)\n\n#define INT24_MAX 0x7fffff\n#define INT24_MIN (-INT24_MAX - 1)\n\n/* Macro to determine type */\n#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)\n\n/* Utility macros */\n#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))\n#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))\n#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))\n#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))\n#define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)\n#define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))\n#define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)\n\n/* We know a positive increment can only be 1 because entries can only be\n * pushed one at a time. */\n#define ZIPLIST_INCR_LENGTH(zl,incr) { \\\n    if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \\\n        ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \\\n}\n\ntypedef struct zlentry {\n    unsigned int prevrawlensize, prevrawlen;\n    unsigned int lensize, len;\n    unsigned int headersize;\n    unsigned char encoding;\n    unsigned char *p;\n} zlentry;\n\n/* Extract the encoding from the byte pointed by 'ptr' and set it into\n * 'encoding'. */\n#define ZIP_ENTRY_ENCODING(ptr, encoding) do {  \\\n    (encoding) = (ptr[0]); \\\n    if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \\\n} while(0)\n\n/* Return bytes needed to store integer encoded by 'encoding' */\nstatic unsigned int zipIntSize(unsigned char encoding) {\n    switch(encoding) {\n    case ZIP_INT_8B:  return 1;\n    case ZIP_INT_16B: return 2;\n    case ZIP_INT_24B: return 3;\n    case ZIP_INT_32B: return 4;\n    case ZIP_INT_64B: return 8;\n    default: return 0; /* 4 bit immediate */\n    }\n    assert(NULL);\n    return 0;\n}\n\n/* Encode the length '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 zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {\n    unsigned char len = 1, buf[5];\n\n    if (ZIP_IS_STR(encoding)) {\n        /* Although encoding is given it may not be set for strings,\n         * so we determine it here using the raw length. */\n        if (rawlen <= 0x3f) {\n            if (!p) return len;\n            buf[0] = ZIP_STR_06B | rawlen;\n        } else if (rawlen <= 0x3fff) {\n            len += 1;\n            if (!p) return len;\n            buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);\n            buf[1] = rawlen & 0xff;\n        } else {\n            len += 4;\n            if (!p) return len;\n            buf[0] = ZIP_STR_32B;\n            buf[1] = (rawlen >> 24) & 0xff;\n            buf[2] = (rawlen >> 16) & 0xff;\n            buf[3] = (rawlen >> 8) & 0xff;\n            buf[4] = rawlen & 0xff;\n        }\n    } else {\n        /* Implies integer encoding, so length is always 1. */\n        if (!p) return len;\n        buf[0] = encoding;\n    }\n\n    /* Store this length at p */\n    memcpy(p,buf,len);\n    return len;\n}\n\n/* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the\n * entries encoding, the 'lensize' variable will hold the number of bytes\n * required to encode the entries length, and the 'len' variable will hold the\n * entries length. */\n#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do {                    \\\n    ZIP_ENTRY_ENCODING((ptr), (encoding));                                     \\\n    if ((encoding) < ZIP_STR_MASK) {                                           \\\n        if ((encoding) == ZIP_STR_06B) {                                       \\\n            (lensize) = 1;                                                     \\\n            (len) = (ptr)[0] & 0x3f;                                           \\\n        } else if ((encoding) == ZIP_STR_14B) {                                \\\n            (lensize) = 2;                                                     \\\n            (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1];                       \\\n        } else if (encoding == ZIP_STR_32B) {                                  \\\n            (lensize) = 5;                                                     \\\n            (len) = ((ptr)[1] << 24) |                                         \\\n                    ((ptr)[2] << 16) |                                         \\\n                    ((ptr)[3] <<  8) |                                         \\\n                    ((ptr)[4]);                                                \\\n        } else {                                                               \\\n            assert(NULL);                                                      \\\n        }                                                                      \\\n    } else {                                                                   \\\n        (lensize) = 1;                                                         \\\n        (len) = zipIntSize(encoding);                                          \\\n    }                                                                          \\\n} while(0);\n\n/* Encode the length of the previous entry and write it to \"p\". Return the\n * number of bytes needed to encode this length if \"p\" is NULL. */\nstatic unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {\n    if (p == NULL) {\n        return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;\n    } else {\n        if (len < ZIP_BIGLEN) {\n            p[0] = len;\n            return 1;\n        } else {\n            p[0] = ZIP_BIGLEN;\n            memcpy(p+1,&len,sizeof(len));\n            //memrev32ifbe(p+1);\n            return 1+sizeof(len);\n        }\n    }\n}\n\n/* Encode the length of the previous entry and write it to \"p\". This only\n * uses the larger encoding (required in __ziplistCascadeUpdate). */\nstatic void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {\n    if (p == NULL) return;\n    p[0] = ZIP_BIGLEN;\n    memcpy(p+1,&len,sizeof(len));\n   // memrev32ifbe(p+1);\n}\n\n/* Decode the number of bytes required to store the length of the previous\n * element, from the perspective of the entry pointed to by 'ptr'. */\n#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do {                          \\\n    if ((ptr)[0] < ZIP_BIGLEN) {                                               \\\n        (prevlensize) = 1;                                                     \\\n    } else {                                                                   \\\n        (prevlensize) = 5;                                                     \\\n    }                                                                          \\\n} while(0);\n\n/* Decode the length of the previous element, from the perspective of the entry\n * pointed to by 'ptr'. */\n#define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do {                     \\\n    ZIP_DECODE_PREVLENSIZE(ptr, prevlensize);                                  \\\n    if ((prevlensize) == 1) {                                                  \\\n        (prevlen) = (ptr)[0];                                                  \\\n    } else if ((prevlensize) == 5) {                                           \\\n        assert(sizeof((prevlensize)) == 4);                                    \\\n        memcpy(&(prevlen), ((char*)(ptr)) + 1, 4);                             \\\n    }                                                                          \\\n} while(0);\n\n/* Return the difference in number of bytes needed to store the length of the\n * previous element 'len', in the entry pointed to by 'p'. */\nstatic int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {\n    unsigned int prevlensize;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    return zipPrevEncodeLength(NULL, len) - prevlensize;\n}\n\n/* Return the total number of bytes used by the entry pointed to by 'p'. */\nstatic unsigned int zipRawEntryLength(unsigned char *p) {\n    unsigned int prevlensize, encoding, lensize, len;\n    ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n    return prevlensize + lensize + len;\n}\n\nint Strtoll(const char* ptr, size_t nBytes, long long* outVal)\n{\n    if (nBytes == 0 || nBytes > 21)\n        return 0;\n    \n    char* pEnd = 0;\n    long long ret = strtoll(ptr, &pEnd, 0);\n    \n    *outVal = ret;\n    return pEnd == ptr + nBytes;\n}\n\n/* Check if string pointed to by 'entry' can be encoded as an integer.\n * Stores the integer value in 'v' and its encoding in 'encoding'. */\nstatic int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {\n    long long value;\n\n    if (entrylen >= 32 || entrylen == 0) return 0;\n    if (Strtoll((const char*)entry, entrylen, &value)) {\n        /* Great, the string can be encoded. Check what's the smallest\n         * of our encoding types that can hold this value. */\n        if (value >= 0 && value <= 12) {\n            *encoding = ZIP_INT_IMM_MIN+value;\n        } else if (value >= INT8_MIN && value <= INT8_MAX) {\n            *encoding = ZIP_INT_8B;\n        } else if (value >= INT16_MIN && value <= INT16_MAX) {\n            *encoding = ZIP_INT_16B;\n        } else if (value >= INT24_MIN && value <= INT24_MAX) {\n            *encoding = ZIP_INT_24B;\n        } else if (value >= INT32_MIN && value <= INT32_MAX) {\n            *encoding = ZIP_INT_32B;\n        } else {\n            *encoding = ZIP_INT_64B;\n        }\n        *v = value;\n        return 1;\n    }\n    return 0;\n}\n\n/* Store integer 'value' at 'p', encoded as 'encoding' */\nstatic void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64;\n    if (encoding == ZIP_INT_8B) {\n        ((int8_t*)p)[0] = (int8_t)value;\n    } else if (encoding == ZIP_INT_16B) {\n        i16 = value;\n        memcpy(p,&i16,sizeof(i16));\n        //memrev16ifbe(p);\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = (int32_t)(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 = (int32_t)(value);\n        memcpy(p,&i32,sizeof(i32));\n       // memrev32ifbe(p);\n    } else if (encoding == ZIP_INT_64B) {\n        i64 = value;\n        memcpy(p,&i64,sizeof(i64));\n        //memrev64ifbe(p);\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        /* Nothing to do, the value is stored in the encoding itself. */\n    } else {\n        assert(NULL);\n    }\n}\n\n/* Read integer encoded as 'encoding' from 'p' */\nstatic int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {\n    int16_t i16;\n    int32_t i32;\n    int64_t i64, ret = 0;\n    if (encoding == ZIP_INT_8B) {\n        ret = ((int8_t*)p)[0];\n    } else if (encoding == ZIP_INT_16B) {\n        memcpy(&i16,p,sizeof(i16));\n       // memrev16ifbe(&i16);\n        ret = i16;\n    } else if (encoding == ZIP_INT_32B) {\n        memcpy(&i32,p,sizeof(i32));\n       // memrev32ifbe(&i32);\n        ret = i32;\n    } else if (encoding == ZIP_INT_24B) {\n        i32 = 0;\n        memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t));\n      //  memrev32ifbe(&i32);\n        ret = i32>>8;\n    } else if (encoding == ZIP_INT_64B) {\n        memcpy(&i64,p,sizeof(i64));\n      //  memrev64ifbe(&i64);\n        ret = i64;\n    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {\n        ret = (encoding & ZIP_INT_IMM_MASK)-1;\n    } else {\n        assert(NULL);\n    }\n    return ret;\n}\n\n/* Return a struct with all information about an entry. */\nstatic zlentry zipEntry(unsigned char *p) {\n    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    return e;\n}\n\n/* Create a new empty ziplist. */\nunsigned char *ziplistNew(void) {\n    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;\n    unsigned char *zl = (unsigned char* )malloc(bytes);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);\n    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);\n    ZIPLIST_LENGTH(zl) = 0;\n    zl[bytes-1] = ZIP_END;\n    return zl;\n}\n\n/* Resize the ziplist. */\nstatic unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {\n    zl = (unsigned char* )realloc(zl,len);\n    ZIPLIST_BYTES(zl) = intrev32ifbe(len);\n    zl[len-1] = ZIP_END;\n    return zl;\n}\n\n/* When an entry is inserted, we need to set the prevlen field of the next\n * entry to equal the length of the inserted entry. It can occur that this\n * length cannot be encoded in 1 byte and the next entry needs to be grow\n * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,\n * because this only happens when an entry is already being inserted (which\n * causes a realloc and memmove). However, encoding the prevlen may require\n * that this entry is grown as well. This effect may cascade throughout\n * the ziplist when there are consecutive entries with a size close to\n * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every\n * consecutive entry.\n *\n * Note that this effect can also happen in reverse, where the bytes required\n * to encode the prevlen field can shrink. This effect is deliberately ignored,\n * because it can cause a \"flapping\" effect where a chain prevlen fields is\n * first grown and then shrunk again after consecutive inserts. Rather, the\n * field is allowed to stay larger than necessary, because a large prevlen\n * field implies the ziplist is holding large entries anyway.\n *\n * The pointer \"p\" points to the first entry that does NOT need to be\n * updated, i.e. consecutive fields MAY need an update. */\nstatic unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;\n    size_t offset, noffset, extra;\n    unsigned char *np;\n    zlentry cur, next;\n\n    while (p[0] != ZIP_END) {\n        cur = zipEntry(p);\n        rawlen = cur.headersize + cur.len;\n        rawlensize = zipPrevEncodeLength(NULL,rawlen);\n\n        /* Abort if there is no next entry. */\n        if (p[rawlen] == ZIP_END) break;\n        next = zipEntry(p+rawlen);\n\n        /* Abort when \"prevlen\" has not changed. */\n        if (next.prevrawlen == rawlen) break;\n\n        if (next.prevrawlensize < rawlensize) {\n            /* The \"prevlen\" field of \"next\" needs more bytes to hold\n             * the raw length of \"cur\". */\n            offset = p-zl;\n            extra = rawlensize-next.prevrawlensize;\n            zl = ziplistResize(zl,curlen+extra);\n            p = zl+offset;\n\n            /* Current pointer and offset for next element. */\n            np = p+rawlen;\n            noffset = np-zl;\n\n            /* Update tail offset when next element is not the tail element. */\n            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);\n            }\n\n            /* Move the tail to the back. */\n            memmove(np+rawlensize,\n                np+next.prevrawlensize,\n                curlen-noffset-next.prevrawlensize-1);\n            zipPrevEncodeLength(np,rawlen);\n\n            /* Advance the cursor */\n            p += rawlen;\n            curlen += extra;\n        } else {\n            if (next.prevrawlensize > rawlensize) {\n                /* This would result in shrinking, which we want to avoid.\n                 * So, set \"rawlen\" in the available bytes. */\n                zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);\n            } else {\n                zipPrevEncodeLength(p+rawlen,rawlen);\n            }\n\n            /* Stop here, as the raw length of \"next\" has not changed. */\n            break;\n        }\n    }\n    return zl;\n}\n\n/* Delete \"num\" entries, starting at \"p\". Returns pointer to the ziplist. */\nstatic unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {\n    unsigned int i, totlen, deleted = 0;\n    size_t offset;\n    int nextdiff = 0;\n    zlentry first, tail;\n\n    first = zipEntry(p);\n    for (i = 0; p[0] != ZIP_END && i < num; i++) {\n        p += zipRawEntryLength(p);\n        deleted++;\n    }\n\n    totlen = p-first.p;\n    if (totlen > 0) {\n        if (p[0] != ZIP_END) {\n            /* Storing `prevrawlen` in this entry may increase or decrease the\n             * number of bytes required compare to the current `prevrawlen`.\n             * There always is room to store this, because it was previously\n             * stored by an entry that is now being deleted. */\n            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);\n            p -= nextdiff;\n            zipPrevEncodeLength(p,first.prevrawlen);\n\n            /* Update offset for tail */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);\n\n            /* When the tail contains more than one entry, we need to take\n             * \"nextdiff\" in account as well. Otherwise, a change in the\n             * size of prevlen doesn't have an effect on the *tail* offset. */\n            tail = zipEntry(p);\n            if (p[tail.headersize+tail.len] != ZIP_END) {\n                ZIPLIST_TAIL_OFFSET(zl) =\n                   intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n            }\n\n            /* Move tail to the front of the ziplist */\n            memmove(first.p,p,\n                intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1);\n        } else {\n            /* The entire tail was deleted. No need to move memory. */\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe((first.p-zl)-first.prevrawlen);\n        }\n\n        /* Resize and update length */\n        offset = first.p-zl;\n        zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff);\n        ZIPLIST_INCR_LENGTH(zl,-deleted);\n        p = zl+offset;\n\n        /* When nextdiff != 0, the raw length of the next entry has changed, so\n         * we need to cascade the update throughout the ziplist */\n        if (nextdiff != 0)\n            zl = __ziplistCascadeUpdate(zl,p);\n    }\n    return zl;\n}\n\n/* Insert item at \"p\". */\nstatic unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {\n    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0;\n    size_t offset;\n    int nextdiff = 0;\n    unsigned char encoding = 0;\n    long long value = 123456789; /* initialized to avoid warning. Using a value\n                                    that is easy to see if for some reason\n                                    we use it uninitialized. */\n    zlentry entry, tail;\n\n    /* Find out prevlen for the entry that is inserted. */\n    if (p[0] != ZIP_END) {\n        entry = zipEntry(p);\n        prevlen = entry.prevrawlen;\n    } else {\n        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);\n        if (ptail[0] != ZIP_END) {\n            prevlen = zipRawEntryLength(ptail);\n        }\n    }\n\n    /* See if the entry can be encoded */\n    if (zipTryEncoding(s,slen,&value,&encoding)) {\n        /* 'encoding' is set to the appropriate integer encoding */\n        reqlen = zipIntSize(encoding);\n    } else {\n        /* 'encoding' is untouched, however zipEncodeLength will use the\n         * string length to figure out how to encode it. */\n        reqlen = slen;\n    }\n    /* We need space for both the length of the previous entry and\n     * the length of the payload. */\n    reqlen += zipPrevEncodeLength(NULL,prevlen);\n    reqlen += zipEncodeLength(NULL,encoding,slen);\n\n    /* When the insert position is not equal to the tail, we need to\n     * make sure that the next entry can hold this entry's length in\n     * its prevlen field. */\n    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;\n\n    /* Store offset because a realloc may change the address of zl. */\n    offset = p-zl;\n    zl = ziplistResize(zl,curlen+reqlen+nextdiff);\n    p = zl+offset;\n\n    /* Apply memory move when necessary and update tail offset. */\n    if (p[0] != ZIP_END) {\n        /* Subtract one because of the ZIP_END bytes */\n        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);\n\n        /* Encode this entry's raw length in the next entry. */\n        zipPrevEncodeLength(p+reqlen,reqlen);\n\n        /* Update offset for tail */\n        ZIPLIST_TAIL_OFFSET(zl) =\n            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);\n\n        /* When the tail contains more than one entry, we need to take\n         * \"nextdiff\" in account as well. Otherwise, a change in the\n         * size of prevlen doesn't have an effect on the *tail* offset. */\n        tail = zipEntry(p+reqlen);\n        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {\n            ZIPLIST_TAIL_OFFSET(zl) =\n                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);\n        }\n    } else {\n        /* This element will be the new tail. */\n        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);\n    }\n\n    /* When nextdiff != 0, the raw length of the next entry has changed, so\n     * we need to cascade the update throughout the ziplist */\n    if (nextdiff != 0) {\n        offset = p-zl;\n        zl = __ziplistCascadeUpdate(zl,p+reqlen);\n        p = zl+offset;\n    }\n\n    /* Write the entry */\n    p += zipPrevEncodeLength(p,prevlen);\n    p += zipEncodeLength(p,encoding,slen);\n    if (ZIP_IS_STR(encoding)) {\n        memcpy(p,s,slen);\n    } else {\n        zipSaveInteger(p,value,encoding);\n    }\n    ZIPLIST_INCR_LENGTH(zl,1);\n    return zl;\n}\n\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    zlentry entry;\n    if (index < 0) {\n        index = (-index)-1;\n        p = ZIPLIST_ENTRY_TAIL(zl);\n        if (p[0] != ZIP_END) {\n            entry = zipEntry(p);\n            while (entry.prevrawlen > 0 && index--) {\n                p -= entry.prevrawlen;\n                entry = zipEntry(p);\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    zlentry entry;\n\n    /* Iterating backwards from ZIP_END should return the tail. When \"p\" is\n     * equal to the first element of the list, we're already at the head,\n     * and should return NULL. */\n    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        entry = zipEntry(p);\n        assert(entry.prevrawlen > 0);\n        return p-entry.prevrawlen;\n    }\n}\n\n/* Get entry pointed to by 'p' and store in either 'e' or 'v' depending\n * on the encoding of the entry. 'e' is always set to NULL to be able\n * to find out whether the string pointer or the integer value was set.\n * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */\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    entry = zipEntry(p);\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, unsigned 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 'entry'. 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    entry = zipEntry(p);\n    if (ZIP_IS_STR(entry.encoding)) {\n        /* Raw compare */\n        if (entry.len == slen) {\n            return memcmp(p+entry.headersize,sstr,slen) == 0;\n        } else {\n            return 0;\n        }\n    } else {\n        /* Try to compare encoded values. Don't compare encoding because\n         * different implementations may encoded integers differently. */\n        if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {\n          zval = zipLoadInteger(p+entry.headersize,entry.encoding);\n          return zval == sval;\n        }\n    }\n    return 0;\n}\n\n/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries\n * between every comparison. Returns NULL when the field could not be found. */\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {\n    int skipcnt = 0;\n    unsigned char vencoding = 0;\n    long long vll = 0;\n\n    while (p[0] != ZIP_END) {\n        unsigned int prevlensize, encoding, lensize, len;\n        unsigned char *q;\n\n        ZIP_DECODE_PREVLENSIZE(p, prevlensize);\n        ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);\n        q = p + prevlensize + lensize;\n\n        if (skipcnt == 0) {\n            /* Compare current entry with specified entry */\n            if (ZIP_IS_STR(encoding)) {\n                if (len == vlen && memcmp(q, vstr, vlen) == 0) {\n                    return p;\n                }\n            } else {\n                /* Find out if the searched field can be encoded. Note that\n                 * we do it only the first time, once done vencoding is set\n                 * to non-zero and vll is set to the integer value. */\n                if (vencoding == 0) {\n                    if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {\n                        /* If the entry can't be encoded we set it to\n                         * UCHAR_MAX so that we don't retry again the next\n                         * time. */\n                        vencoding = UCHAR_MAX;\n                    }\n                    /* Must be non-zero by now */\n                    assert(vencoding);\n                }\n\n                /* Compare current entry with specified entry, do it only\n                 * if vencoding != UCHAR_MAX because if there is no encoding\n                 * possible for the field it can't be a valid integer. */\n                if (vencoding != UCHAR_MAX) {\n                    long long ll = zipLoadInteger(q, encoding);\n                    if (ll == vll) {\n                        return p;\n                    }\n                }\n            }\n\n            /* Reset skip count */\n            skipcnt = skip;\n        } else {\n            /* Skip entry */\n            skipcnt--;\n        }\n\n        /* Move to next entry */\n        p = q + len;\n    }\n\n    return NULL;\n}\n\n/* Return length of ziplist. */\nunsigned int ziplistLen(unsigned char *zl) {\n    unsigned int len = 0;\n    if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {\n        len = intrev16ifbe(ZIPLIST_LENGTH(zl));\n    } else {\n        unsigned char *p = zl+ZIPLIST_HEADER_SIZE;\n        while (*p != ZIP_END) {\n            p += zipRawEntryLength(p);\n            len++;\n        }\n\n        /* Re-store length if small enough */\n        if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);\n    }\n    return len;\n}\n\n/* Return ziplist blob size in bytes. */\nsize_t ziplistBlobLen(unsigned char *zl) {\n    return intrev32ifbe(ZIPLIST_BYTES(zl));\n}\n\nvoid ziplistRepr(unsigned char *zl) {\n    unsigned char *p;\n    int index = 0;\n    zlentry entry;\n\n    printf(\n        \"{total bytes %d} \"\n        \"{length %u}\\n\"\n        \"{tail offset %u}\\n\",\n        intrev32ifbe(ZIPLIST_BYTES(zl)),\n        intrev16ifbe(ZIPLIST_LENGTH(zl)),\n        intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)));\n    p = ZIPLIST_ENTRY_HEAD(zl);\n    while(*p != ZIP_END) {\n        entry = zipEntry(p);\n        printf(\n            \"{\"\n                \"addr 0x%08lx, \"\n                \"index %2d, \"\n                \"offset %5ld, \"\n                \"rl: %5u, \"\n                \"hs %2u, \"\n                \"pl: %5u, \"\n                \"pls: %2u, \"\n                \"payload %5u\"\n            \"} \",\n            (long unsigned)p,\n            index,\n            (unsigned long) (p-zl),\n            entry.headersize+entry.len,\n            entry.headersize,\n            entry.prevrawlen,\n            entry.prevrawlensize,\n            entry.len);\n        p += entry.headersize;\n        if (ZIP_IS_STR(entry.encoding)) {\n            if (entry.len > 40) {\n                if (fwrite(p,40,1,stdout) == 0) perror(\"fwrite\");\n                printf(\"...\");\n            } else {\n                if (entry.len &&\n                    fwrite(p,entry.len,1,stdout) == 0) perror(\"fwrite\");\n            }\n        } else {\n            printf(\"%lld\", (long long) zipLoadInteger(p,entry.encoding));\n        }\n        printf(\"\\n\");\n        p += entry.len;\n        index++;\n    }\n    printf(\"{end}\\n\\n\");\n}\n\n#ifdef ZIPLIST_TEST_MAIN\n#include <sys/time.h>\n#include \"adlist.h\"\n#include \"sds.h\"\n\n#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }\n\nunsigned char *createList() {\n    unsigned char *zl = ziplistNew();\n    zl = ziplistPush(zl, (unsigned char*)\"foo\", 3, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"quux\", 4, ZIPLIST_TAIL);\n    zl = ziplistPush(zl, (unsigned char*)\"hello\", 5, ZIPLIST_HEAD);\n    zl = ziplistPush(zl, (unsigned char*)\"1024\", 4, ZIPLIST_TAIL);\n    return zl;\n}\n\nunsigned char *createIntList() {\n    unsigned char *zl = ziplistNew();\n    char buf[32];\n\n    sprintf(buf, \"100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"128000\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"-100\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"4294967296\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);\n    sprintf(buf, \"non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    sprintf(buf, \"much much longer non integer\");\n    zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);\n    return zl;\n}\n\nlong long usec(void) {\n    struct timeval tv;\n    gettimeofday(&tv,NULL);\n    return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;\n}\n\nvoid stress(int pos, int num, int maxsize, int dnum) {\n    int i,j,k;\n    unsigned char *zl;\n    char posstr[2][5] = { \"HEAD\", \"TAIL\" };\n    long long start;\n    for (i = 0; i < maxsize; i+=dnum) {\n        zl = ziplistNew();\n        for (j = 0; j < i; j++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,ZIPLIST_TAIL);\n        }\n\n        /* Do num times a push+pop from pos */\n        start = usec();\n        for (k = 0; k < num; k++) {\n            zl = ziplistPush(zl,(unsigned char*)\"quux\",4,pos);\n            zl = ziplistDeleteRange(zl,0,1);\n        }\n        printf(\"List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\\n\",\n            i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start);\n        zfree(zl);\n    }\n}\n\nvoid pop(unsigned char *zl, int where) {\n    unsigned char *p, *vstr;\n    unsigned int vlen;\n    long long vlong;\n\n    p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1);\n    if (ziplistGet(p,&vstr,&vlen,&vlong)) {\n        if (where == ZIPLIST_HEAD)\n            printf(\"Pop head: \");\n        else\n            printf(\"Pop tail: \");\n\n        if (vstr)\n            if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror(\"fwrite\");\n        else\n            printf(\"%lld\", vlong);\n\n        printf(\"\\n\");\n        ziplistDeleteRange(zl,-1,1);\n    } else {\n        printf(\"ERROR: Could not pop\\n\");\n        exit(1);\n    }\n}\n\nint randstring(char *target, unsigned int min, unsigned int max) {\n    int p, len = min+rand()%(max-min+1);\n    int minval, maxval;\n    switch(rand() % 3) {\n    case 0:\n        minval = 0;\n        maxval = 255;\n    break;\n    case 1:\n        minval = 48;\n        maxval = 122;\n    break;\n    case 2:\n        minval = 48;\n        maxval = 52;\n    break;\n    default:\n        assert(NULL);\n    }\n\n    while(p < len)\n        target[p++] = minval+rand()%(maxval-minval+1);\n    return len;\n}\n\nvoid verify(unsigned char *zl, zlentry *e) {\n    int i;\n    int len = ziplistLen(zl);\n    zlentry _e;\n\n    for (i = 0; i < len; i++) {\n        memset(&e[i], 0, sizeof(zlentry));\n        e[i] = zipEntry(ziplistIndex(zl, i));\n\n        memset(&_e, 0, sizeof(zlentry));\n        _e = zipEntry(ziplistIndex(zl, -len+i));\n\n        assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0);\n    }\n}\n\nint main(int argc, char **argv) {\n    unsigned char *zl, *p;\n    unsigned char *entry;\n    unsigned int elen;\n    long long value;\n\n    /* If an argument is given, use it as the random seed. */\n    if (argc == 2)\n        srand(atoi(argv[1]));\n\n    zl = createIntList();\n    ziplistRepr(zl);\n\n    zl = createList();\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_HEAD);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    pop(zl,ZIPLIST_TAIL);\n    ziplistRepr(zl);\n\n    printf(\"Get element at index 3:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 3);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index 3\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Get element at index 4 (out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Get element at index -1 (last element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -1\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Get element at index -4 (first element):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"ERROR: Could not access index -4\\n\");\n            return 1;\n        }\n        if (entry) {\n            if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            printf(\"\\n\");\n        } else {\n            printf(\"%lld\\n\", value);\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Get element at index -5 (reverse out of range):\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -5);\n        if (p == NULL) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR: Out of range index should return NULL, returned offset: %ld\\n\", p-zl);\n            return 1;\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate list from 0 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 0);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate list from 1 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate list from 2 to end:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 2);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistNext(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate starting out of range:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, 4);\n        if (!ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"No entry\\n\");\n        } else {\n            printf(\"ERROR\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate from back to front:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Iterate from back to front, deleting all items:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl, -1);\n        while (ziplistGet(p, &entry, &elen, &value)) {\n            printf(\"Entry: \");\n            if (entry) {\n                if (elen && fwrite(entry,elen,1,stdout) == 0) perror(\"fwrite\");\n            } else {\n                printf(\"%lld\", value);\n            }\n            zl = ziplistDelete(zl,&p);\n            p = ziplistPrev(zl,p);\n            printf(\"\\n\");\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Delete inclusive range 0,0:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 1);\n        ziplistRepr(zl);\n    }\n\n    printf(\"Delete inclusive range 0,1:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 0, 2);\n        ziplistRepr(zl);\n    }\n\n    printf(\"Delete inclusive range 1,2:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 2);\n        ziplistRepr(zl);\n    }\n\n    printf(\"Delete with start index out of range:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 5, 1);\n        ziplistRepr(zl);\n    }\n\n    printf(\"Delete with num overflow:\\n\");\n    {\n        zl = createList();\n        zl = ziplistDeleteRange(zl, 1, 5);\n        ziplistRepr(zl);\n    }\n\n    printf(\"Delete foo while iterating:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        while (ziplistGet(p,&entry,&elen,&value)) {\n            if (entry && strncmp(\"foo\",(char*)entry,elen) == 0) {\n                printf(\"Delete foo\\n\");\n                zl = ziplistDelete(zl,&p);\n            } else {\n                printf(\"Entry: \");\n                if (entry) {\n                    if (elen && fwrite(entry,elen,1,stdout) == 0)\n                        perror(\"fwrite\");\n                } else {\n                    printf(\"%lld\",value);\n                }\n                p = ziplistNext(zl,p);\n                printf(\"\\n\");\n            }\n        }\n        printf(\"\\n\");\n        ziplistRepr(zl);\n    }\n\n    printf(\"Regression test for >255 byte strings:\\n\");\n    {\n        char v1[257],v2[257];\n        memset(v1,'x',256);\n        memset(v2,'y',256);\n        zl = ziplistNew();\n        zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL);\n        zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL);\n\n        /* Pop values again and compare their value. */\n        p = ziplistIndex(zl,0);\n        assert(ziplistGet(p,&entry,&elen,&value));\n        assert(strncmp(v1,(char*)entry,elen) == 0);\n        p = ziplistIndex(zl,1);\n        assert(ziplistGet(p,&entry,&elen,&value));\n        assert(strncmp(v2,(char*)entry,elen) == 0);\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Regression test deleting next to last entries:\\n\");\n    {\n        char v[3][257];\n        zlentry e[3];\n        int i;\n\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            memset(v[i], 'a' + i, sizeof(v[0]));\n        }\n\n        v[0][256] = '\\0';\n        v[1][  1] = '\\0';\n        v[2][256] = '\\0';\n\n        zl = ziplistNew();\n        for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) {\n            zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL);\n        }\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n        assert(e[2].prevrawlensize == 1);\n\n        /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */\n        unsigned char *p = e[1].p;\n        zl = ziplistDelete(zl, &p);\n\n        verify(zl, e);\n\n        assert(e[0].prevrawlensize == 1);\n        assert(e[1].prevrawlensize == 5);\n\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Create long list and check indices:\\n\");\n    {\n        zl = ziplistNew();\n        char buf[32];\n        int i,len;\n        for (i = 0; i < 1000; i++) {\n            len = sprintf(buf,\"%d\",i);\n            zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);\n        }\n        for (i = 0; i < 1000; i++) {\n            p = ziplistIndex(zl,i);\n            assert(ziplistGet(p,NULL,NULL,&value));\n            assert(i == value);\n\n            p = ziplistIndex(zl,-i-1);\n            assert(ziplistGet(p,NULL,NULL,&value));\n            assert(999-i == value);\n        }\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Compare strings with ziplist entries:\\n\");\n    {\n        zl = createList();\n        p = ziplistIndex(zl,0);\n        if (!ziplistCompare(p,(unsigned char*)\"hello\",5)) {\n            printf(\"ERROR: not \\\"hello\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"hella\",5)) {\n            printf(\"ERROR: \\\"hella\\\"\\n\");\n            return 1;\n        }\n\n        p = ziplistIndex(zl,3);\n        if (!ziplistCompare(p,(unsigned char*)\"1024\",4)) {\n            printf(\"ERROR: not \\\"1024\\\"\\n\");\n            return 1;\n        }\n        if (ziplistCompare(p,(unsigned char*)\"1025\",4)) {\n            printf(\"ERROR: \\\"1025\\\"\\n\");\n            return 1;\n        }\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Stress with random payloads of different encoding:\\n\");\n    {\n        int i,j,len,where;\n        unsigned char *p;\n        char buf[1024];\n        int buflen;\n        list *ref;\n        listNode *refnode;\n\n        /* Hold temp vars from ziplist */\n        unsigned char *sstr;\n        unsigned int slen;\n        long long sval;\n\n        for (i = 0; i < 20000; i++) {\n            zl = ziplistNew();\n            ref = listCreate();\n            listSetFreeMethod(ref,sdsfree);\n            len = rand() % 256;\n\n            /* Create lists */\n            for (j = 0; j < len; j++) {\n                where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;\n                if (rand() % 2) {\n                    buflen = randstring(buf,1,sizeof(buf)-1);\n                } else {\n                    switch(rand() % 3) {\n                    case 0:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) >> 20);\n                        break;\n                    case 1:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()));\n                        break;\n                    case 2:\n                        buflen = sprintf(buf,\"%lld\",(0LL + rand()) << 20);\n                        break;\n                    default:\n                        assert(NULL);\n                    }\n                }\n\n                /* Add to ziplist */\n                zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);\n\n                /* Add to reference list */\n                if (where == ZIPLIST_HEAD) {\n                    listAddNodeHead(ref,sdsnewlen(buf, buflen));\n                } else if (where == ZIPLIST_TAIL) {\n                    listAddNodeTail(ref,sdsnewlen(buf, buflen));\n                } else {\n                    assert(NULL);\n                }\n            }\n\n            assert(listLength(ref) == ziplistLen(zl));\n            for (j = 0; j < len; j++) {\n                /* Naive way to get elements, but similar to the stresser\n                 * executed from the Tcl test suite. */\n                p = ziplistIndex(zl,j);\n                refnode = listIndex(ref,j);\n\n                assert(ziplistGet(p,&sstr,&slen,&sval));\n                if (sstr == NULL) {\n                    buflen = sprintf(buf,\"%lld\",sval);\n                } else {\n                    buflen = slen;\n                    memcpy(buf,sstr,buflen);\n                    buf[buflen] = '\\0';\n                }\n                assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);\n            }\n            zfree(zl);\n            listRelease(ref);\n        }\n        printf(\"SUCCESS\\n\\n\");\n    }\n\n    printf(\"Stress with variable ziplist size:\\n\");\n    {\n        stress(ZIPLIST_HEAD,100000,16384,256);\n        stress(ZIPLIST_TAIL,100000,16384,256);\n    }\n\n    return 0;\n}\n\n#endif\n"
  },
  {
    "path": "QedisCore/redisZipList.h",
    "content": "/*\n * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#define ZIPLIST_HEAD 0\n#define ZIPLIST_TAIL 1\n\nunsigned char *ziplistNew(void);\nunsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);\nunsigned char *ziplistIndex(unsigned char *zl, int index);\nunsigned char *ziplistNext(unsigned char *zl, unsigned char *p);\nunsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);\nunsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval);\nunsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);\nunsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num);\nunsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);\nunsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);\nunsigned int ziplistLen(unsigned char *zl);\nsize_t ziplistBlobLen(unsigned char *zl);\n"
  },
  {
    "path": "QedisSvr/CMakeLists.txt",
    "content": "#PROJECT(QEDISSERVER)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nAUX_SOURCE_DIRECTORY(. QEDISSERVER_SRC)\n\nLINK_DIRECTORIES(../../leveldb)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase; ${PROJECT_SOURCE_DIR}/QedisCore)\n\nADD_EXECUTABLE(qedis_server ${QEDISSERVER_SRC})\nSET(EXECUTABLE_OUTPUT_PATH  ../../bin)\n\nTARGET_LINK_LIBRARIES(qedis_server qediscore; qbaselib; leveldb)\nADD_DEPENDENCIES(qedis_server qbaselib; qediscore; leveldb)\nIF(${QEDIS_CLUSTER} EQUAL 1)\n    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QSentinel)\n    TARGET_LINK_LIBRARIES(qedis_server qcluster)\n    ADD_DEPENDENCIES(qedis_server qcluster)\nENDIF()\n\n"
  },
  {
    "path": "QedisSvr/Qedis.cc",
    "content": "//\n//  Qedis.cc\n//\n//  Copyright (c) 2014-2018 Bert Young. All rights reserved.\n//\n\n#include <iostream>\n#include <unistd.h>\n#include <sys/wait.h>\n\n#include \"Log/Logger.h\"\n#include \"Timer.h\"\n\n#include \"QClient.h\"\n#include \"QSlaveClient.h\"\n#include \"QStore.h\"\n#include \"QCommand.h\"\n\n#include \"QPubsub.h\"\n#include \"QMigration.h\"\n#include \"QDB.h\"\n#include \"QAOF.h\"\n#include \"QConfig.h\"\n#include \"QSlowLog.h\"\n#include \"QModule.h\"\n\n#include \"QedisLogo.h\"\n#include \"Qedis.h\"\n\n#if QEDIS_CLUSTER\n#include \"QClusterClient.h\"\n\n#endif\n\nconst unsigned Qedis::kRunidSize = 40;\n\nQedis::Qedis() : port_(0), masterPort_(0)\n{\n}\n\nQedis::~Qedis()\n{\n}\n\nstatic void Usage()\n{\n    std::cerr << \"Usage:  ./qedis-server [/path/to/redis.conf] [options]\\n\\\n        ./qedis-server -v or --version\\n\\\n        ./qedis-server -h or --help\\n\\\nExamples:\\n\\\n        ./qedis-server (run the server with default conf)\\n\\\n        ./qedis-server /etc/redis/6379.conf\\n\\\n        ./qedis-server --port 7777\\n\\\n        ./qedis-server --port 7777 --slaveof 127.0.0.1 8888\\n\\\n        ./qedis-server /etc/myredis.conf --loglevel verbose\\n\";\n}\n\nbool  Qedis::ParseArgs(int ac, char* av[])\n{\n    for (int i = 0; i < ac; i ++)\n    {\n        if (cfgFile_.empty() && ::access(av[i], R_OK) == 0)\n        {\n            cfgFile_ = av[i];\n            continue;\n        }\n        else if (strncasecmp(av[i], \"-v\", 2) == 0 ||\n                 strncasecmp(av[i], \"--version\", 9) == 0)\n        {\n            std::cerr << \"Qedis Server v=\"\n                      << QEDIS_VERSION\n                      << \" bits=\"\n                      << (sizeof(void*) == 8 ? 64 : 32)\n                      << std::endl;\n\n            exit(0);\n            return true;\n        }\n        else if (strncasecmp(av[i], \"-h\", 2) == 0 ||\n                 strncasecmp(av[i], \"--help\", 6) == 0)\n        {\n            Usage();\n            exit(0);\n            return true;\n        }\n        else if (strncasecmp(av[i], \"--port\", 6) == 0)\n        {\n            if (++i == ac)\n            {\n                return false;\n            }\n            port_ = static_cast<unsigned short>(std::atoi(av[i]));\n        }\n        else if (strncasecmp(av[i], \"--loglevel\", 10) == 0)\n        {\n            if (++i == ac)\n            {\n                return false;\n            }\n            logLevel_ = std::string(av[i]);\n        }\n        else if (strncasecmp(av[i], \"--slaveof\", 9) == 0)\n        {\n            if (i + 2 >= ac)\n            {\n                return false;\n            }\n            \n            master_ = std::string(av[++i]);\n            masterPort_ = static_cast<unsigned short>(std::atoi(av[++i]));\n        }\n        else\n        {\n            std::cerr << \"Unknow option \" << av[i] << std::endl;\n            return false;\n        }\n    }\n    \n    return true;\n}\n\n\nstd::shared_ptr<StreamSocket> Qedis::_OnNewConnection(int connfd, int tag)\n{\n    using namespace qedis;\n\n    SocketAddr peer;\n    Socket::GetPeerAddr(connfd, peer);\n\n    switch (tag)\n    {\n    case ConnectionTag::kQedisClient:\n        {\n            // incoming clients or connect to master\n            auto cli(std::make_shared<QClient>());\n            if (!cli->Init(connfd, peer))\n                cli.reset();\n\n            return cli;\n        }\n        break;\n#if QEDIS_CLUSTER\n    case ConnectionTag::kSentinelClient:\n        {\n            DBG << \"Connect success to cluster \" << peer.ToString();\n            auto conn = std::make_shared<QClusterClient>();\n            if (!conn->Init(connfd, peer))\n                conn.reset();\n\n            return conn;\n        }\n        break;\n    case ConnectionTag::kSlaveClient:\n        {\n            INF << \"Connect to slave \" << peer.ToString();\n            auto cli(std::make_shared<QSlaveClient>());\n            if (!cli->Init(connfd, peer))\n                cli.reset();\n\n            return cli;\n        }\n        break;\n#endif\n    case ConnectionTag::kMigrateClient:\n        {\n            INF << \"Connect to migrate \" << peer.ToString();\n            auto cli(std::make_shared<QMigrateClient>());\n            if (!cli->Init(connfd, peer))\n                cli.reset();\n\n            return cli;\n        }\n        break;\n    default:\n        ERR << \"Unknown tag \" << tag;\n        break;\n    }\n    \n    return nullptr;\n}\n\nTime  g_now;\n\nstatic void QdbCron()\n{\n    using namespace qedis;\n    \n    if (g_qdbPid != -1)\n        return;\n    \n    if (g_now.MilliSeconds() > (g_lastQDBSave + unsigned(g_config.saveseconds)) * 1000UL &&\n        QStore::dirty_ >= g_config.savechanges)\n    {\n        int ret = fork();\n        if (ret == 0)\n        {\n            {\n                QDBSaver  qdb;\n                qdb.Save(g_config.rdbfullname.c_str());\n                std::cerr << \"ServerCron child save rdb done, exiting child\\n\";\n            }  //  make qdb to be destructed before exit\n            _exit(0);\n        }\n        else if (ret == -1)\n        {\n            ERR << \"fork qdb save process failed\";\n        }\n        else\n        {\n            g_qdbPid = ret;\n        }\n            \n        INF << \"ServerCron save rdb file \" << g_config.rdbfullname;\n    }\n}\n\nstatic void LoadDbFromDisk()\n{\n    using namespace qedis;\n    \n    //  USE AOF RECOVERY FIRST, IF FAIL, THEN RDB\n    QAOFLoader aofLoader;\n    if (aofLoader.Load(g_config.appendfilename.c_str()))\n    {\n        const auto& cmds = aofLoader.GetCmds();\n        for (const auto& cmd : cmds)\n        {\n            const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd[0]);\n            QCommandTable::ExecuteCmd(cmd, info);\n        }\n    }\n    else\n    {\n        QDBLoader  loader;\n        loader.Load(g_config.rdbfullname.c_str());\n    }\n}\n\n#if QEDIS_CLUSTER\nstatic void OnConnectClusterFail(const std::vector<SocketAddr>& addrs, size_t& i)\n{\n    WRN << \"Connect cluster failed \" << addrs[i].ToString();\n    if (++i >= addrs.size())\n        i = 0;\n    \n    Timer* timer = TimerManager::Instance().CreateTimer();\n    timer->Init(2 * 1000, 1);\n    timer->SetCallback([=, &i]() {\n        USR << \"OnTimer connect to \" << addrs[i].GetIP() << \":\" << addrs[i].GetPort();\n        Server::Instance()->TCPConnect(addrs[i], std::bind(OnConnectClusterFail, addrs, std::ref(i)), ConnectionTag::kSentinelClient);\n    });\n    TimerManager::Instance().AsyncAddTimer(timer);\n};\n#endif\n\nbool Qedis::_Init()\n{\n    using namespace qedis;\n    \n    char runid[kRunidSize + 1] = \"\";\n    getRandomHexChars(runid, kRunidSize);\n    g_config.runid.assign(runid, kRunidSize);\n    \n    if (port_ != 0)\n        g_config.port = port_;\n\n    if (!logLevel_.empty())\n        g_config.loglevel = logLevel_;\n    \n    if (!master_.empty())\n    {\n        g_config.masterIp = master_;\n        g_config.masterPort = masterPort_;\n    }\n    \n    // process log\n    {\n        unsigned int level = ConvertLogLevel(g_config.loglevel), dest = 0;\n\n        if (g_config.logdir == \"stdout\")\n            dest = logConsole;\n        else\n            dest = logFILE;\n        \n        g_log = LogManager::Instance().CreateLog(level, dest, g_config.logdir.c_str());\n    }\n    \n    SocketAddr addr(g_config.ip.c_str(), g_config.port);\n    \n    if (!Server::TCPBind(addr, ConnectionTag::kQedisClient))\n    {\n        ERR << \"can not bind socket on port \" << addr.GetPort();\n        return false;\n    }\n\n    QCommandTable::Init();\n    QCommandTable::AliasCommand(g_config.aliases);\n    QSTORE.Init(g_config.databases);\n    QSTORE.InitExpireTimer();\n    QSTORE.InitBlockedTimer();\n    QSTORE.InitEvictionTimer();\n    QSTORE.InitDumpBackends();\n    QPubsub::Instance().InitPubsubTimer();\n    QMigrationManager::Instance().InitMigrationTimer();\n    \n    // Only if there is no backend, load aof or rdb\n    if (g_config.backend == qedis::BackEndNone)\n        LoadDbFromDisk();\n\n    QAOFThreadController::Instance().Start();\n\n    QSlowLog::Instance().SetThreshold(g_config.slowlogtime);\n    QSlowLog::Instance().SetLogLimit(static_cast<std::size_t>(g_config.slowlogmaxlen));\n    \n    {\n        auto cronTimer = TimerManager::Instance().CreateTimer();\n        cronTimer->Init(1000 / qedis::g_config.hz);\n        cronTimer->SetCallback([]() {\n                QdbCron();\n        });\n        TimerManager::Instance().AddTimer(cronTimer);\n    }\n\n    {\n        auto repTimer = TimerManager::Instance().CreateTimer();\n        repTimer->Init(1500);\n        repTimer->SetCallback([&]() {\n            QREPL.Cron();\n        });\n        TimerManager::Instance().AddTimer(repTimer);\n    }\n    \n    // master ip\n    if (!g_config.masterIp.empty())\n    {\n        QREPL.SetMasterAddr(g_config.masterIp.c_str(),\n                            g_config.masterPort);\n    }\n    \n    // load so modules\n    const auto& modules = g_config.modules;\n    for (const auto& mod: modules)\n    {\n        try\n        {\n            MODULES.Load(mod.c_str());\n            std::cerr << \"Load \" << mod << \" successful\\n\";\n        }\n        catch (const ModuleNoLoad& e)\n        {\n            std::cerr << \"Load \" << mod << \" failed\\n\";\n        }\n        catch (const ModuleExist& e)\n        {\n            std::cerr << \"Load \" << mod << \" failed because exist\\n\";\n        }\n        catch (const std::runtime_error& e)\n        {\n            std::cerr << \"Load \" << mod << \" failed because runtime error\\n\";\n        }\n        catch (...)\n        {\n            std::cerr << \"Load \" << mod << \" failed, unknown exception\\n\";\n        }\n    }\n\n    // output logo to console\n    char logo[512] = \"\";\n    snprintf(logo, sizeof logo - 1, qedisLogo, QEDIS_VERSION, static_cast<int>(sizeof(void*)) * 8, static_cast<int>(g_config.port)); \n    std::cerr << logo;\n\n#if QEDIS_CLUSTER\n    // cluster\n    if (g_config.enableCluster)\n    {\n        std::vector<SocketAddr> addrs;\n        for (const auto& s : g_config.centers)\n        {\n            addrs.push_back(SocketAddr(s));\n        }\n\n        std::function<void ()> retry = std::bind(OnConnectClusterFail, addrs, std::ref(clusterIndex_));\n        Server::Instance()->TCPConnect(addrs[clusterIndex_], retry, ConnectionTag::kSentinelClient);\n    }\n#endif\n\n    return  true;\n}\n\nstatic void CheckChild()\n{\n    using namespace qedis;\n\n    if (g_qdbPid == -1 && g_rewritePid == -1)\n        return;\n\n    int statloc = 0;\n    pid_t pid = wait3(&statloc,WNOHANG,NULL);\n\n    if (pid != 0 && pid != -1)\n    {\n        int exit = WEXITSTATUS(statloc);\n        int signal = 0;\n\n        if (WIFSIGNALED(statloc)) signal = WTERMSIG(statloc);\n        \n        if (pid == g_qdbPid)\n        {\n            QDBSaver::SaveDoneHandler(exit, signal);\n            if (QREPL.IsBgsaving())\n                QREPL.OnRdbSaveDone();\n            else\n                QREPL.TryBgsave();\n        }\n        else if (pid == g_rewritePid)\n        {\n            INF << pid << \" pid rewrite process success done.\";\n            QAOFThreadController::RewriteDoneHandler(exit, signal);\n        }\n        else\n        {\n            ERR << pid << \" is not rdb or aof process \";\n            assert (!!!\"Is there any back process except rdb and aof?\");\n        }\n    }\n}\n\nbool Qedis::_RunLogic()\n{\n    g_now.Now();\n    TimerManager::Instance().UpdateTimers(g_now);\n    \n    CheckChild();\n    \n    return Server::_RunLogic();\n}\n\n\nvoid Qedis::_Recycle()\n{\n    std::cerr << \"Qedis::_Recycle: server is exiting.. BYE BYE\\n\";\n    qedis::QAOFThreadController::Instance().Stop();\n}\n\n\nint main(int ac, char* av[])\n{\n    Qedis  svr;\n    if (!svr.ParseArgs(ac - 1, av + 1))\n    {\n        Usage();\n        return -1;\n    }\n\n    if (!svr.GetConfigName().empty())\n    {\n        if (!LoadQedisConfig(svr.GetConfigName().c_str(), qedis::g_config))\n        {\n            std::cerr << \"Load config file [\" << svr.GetConfigName() << \"] failed!\\n\";\n            return -2;\n        }\n    }\n    \n    svr.MainLoop(qedis::g_config.daemonize);\n    \n    return 0;\n}\n\n\n"
  },
  {
    "path": "QedisSvr/Qedis.h",
    "content": "//\n//  qedis.h\n//\n//  Created by Bert Young on 16-1-22.\n//  Copyright (c) 2016年 Bert Young. All rights reserved.\n//\n\n#include \"QString.h\"\n#include \"Server.h\"\n\n#define QEDIS_VERSION \"1.0.0\"\n\nclass Qedis : public Server\n{\npublic:\n    Qedis();\n    ~Qedis();\n    \n    bool  ParseArgs(int ac, char* av[]);\n    const qedis::QString& GetConfigName() const { return cfgFile_; }\n\nprivate:\n    std::shared_ptr<StreamSocket> _OnNewConnection(int fd, int tag) override;\n    bool    _Init() override;\n    bool    _RunLogic() override;\n    void    _Recycle() override;\n    \n    qedis::QString cfgFile_;\n    unsigned short port_;\n    qedis::QString logLevel_;\n    \n    qedis::QString master_;\n    unsigned short masterPort_;\n\n#if QEDIS_CLUSTER\n    // cluster\n    size_t clusterIndex_ = 0;\n#endif\n    \n    static const unsigned kRunidSize;\n};\n"
  },
  {
    "path": "QedisSvr/QedisLogo.h",
    "content": "#ifndef BERT_QEDISLOGO_H\n#define BERT_QEDISLOGO_H\n\nconst char* qedisLogo = \"\\n  _____     _____   _____   _   _____ \\n\"\n                        \" /  _  \\\\   | ____| |  _  \\\\ | | /  ___/\\n\"\n                        \" | | | |   | |__   | | | | | | | |___    Qedis(%s) %d bits, another redis written in C++11\\n\" // version and server bits\n                        \" | | | |   |  __|  | | | | | | \\\\___  \\\\   Port: %d\\n\"\n                        \" | |_| |_  | |___  | |_| | | |  ___| |   Author: Bert Young\\n\"\n                        \" \\\\_______| |_____| |_____/ |_| /_____/   https://github.com/loveyacper/Qedis\\n\\n\\n\";\n\n#endif\n\n"
  },
  {
    "path": "README.md",
    "content": "\n     _____     _____   _____   _   ______                  \n    /  _  \\   | ____| |  _  \\ | | /  ___/                        \n    | | | |   | |__   | | | | | | | |____\n    | | | |   |  __|  | | | | | |  \\__   \\\n    | |_| |_  | |___  | |_| | | |  ___|  |\n    \\_______| |_____| |_____/ |_| /_____/\n\n\n[![Build Status](https://travis-ci.org/loveyacper/Qedis.svg?branch=master)](https://travis-ci.org/loveyacper/Qedis)\n\n[看中文说明请点我](README.zh.md)\n\nA C++11 implementation of distributed redis server, use Leveldb for persist storage.(including cluster)\n\n## Requirements\n* C++11 & CMake\n* Linux or OS X\n\n## Cluster Features\n Use zookeeper for leader election, to reach high availability.\n\n Of course you can also use redis-sentinel.\n\n See details in [cluster Readme](QCluster/README.md), still in development.\n\n## Fully compatible with redis\n You can test Qedis with redis-cli, redis-benchmark, or use redis as master with Qedis as slave or conversely, it also can work with redis sentinel.\n In a word, Qedis is full compatible with Redis.\n\n## Support module for write your own extensions\n Qedis supports module now, still in progress, much work to do.\n I added three commands(ldel, skeys, hgets) for demonstration.\n\n## Persistence: Not limited to memory\n Leveldb can be configured as backend for Qedis.\n\n## High Performance\n- Qedis is approximately 20-25% faster than redis if run benchmark with pipeline requests(set -P = 50 or higher).\n- Average 80K requests per seconds for write, and 90K requests per seconds for read.\n- Before run test, please ensure that std::list::size() is O(1), obey the C++11 standards.\n\nRun this command, compare with redis use pipeline commands, try it.\n```bash\n./redis-benchmark -q -n 1000000 -P 50 -c 50\n```\n\n![image](https://github.com/loveyacper/Qedis/blob/master/performance.png)\n\n## Support LRU cache\n When memory is low, you can make Qedis to free memory by evict some key according to LRU.\n\n## Master-slave Replication, transaction, RDB/AOF, slow log, publish-subscribe\n Qedis supports them all :-)\n \n## Command List\n#### show all supported commands list, about 140 commands\n- cmdlist\n\n## TODO\n* Support lua\n* Golang Cluster client\n\n"
  },
  {
    "path": "README.zh.md",
    "content": "\n     _____     _____   _____   _   ______                  \n    /  _  \\   | ____| |  _  \\ | | /  ___/                        \n    | | | |   | |__   | | | | | | | |____\n    | | | |   |  __|  | | | | | |  \\__   \\\n    | |_| |_  | |___  | |_| | | |  ___|  |\n    \\_______| |_____| |_____/ |_| /_____/\n\n\n[![Build Status](https://travis-ci.org/loveyacper/Qedis.svg?branch=master)](https://travis-ci.org/loveyacper/Qedis)\n\n[Click me switch to English](README.md)\n\nC++11实现的增强版分布式Redis服务器,使用Leveldb作为持久化存储引擎。\n\n## 环境需求\n* C++11、CMake\n* Linux 或 MAC OS\n\n## 集群特性\n 可以搭建Zookeeper，监视一组互为主备的Qedis进程以实现高可用;\n\n 当然也可以使用官方redis-sentinel。\n\n 详见[cluster Readme](QCluster/README.md)\n\n scale-out集群正在开发中...\n\n## 与Redis完全兼容\n 你可以用redis的各种工具来测试Qedis，比如官方的redis-cli, redis-benchmark。\n\n Qedis可以和redis之间进行复制，可以读取redis的rdb文件或aof文件。当然，Qedis生成的aof或rdb文件也可以被redis读取。\n\n 你还可以用redis-sentinel来实现Qedis的高可用！\n\n 总之，Qedis与Redis完全兼容。\n\n## 高性能\n- Qedis性能大约比Redis3.2高出20%(使用redis-benchmark测试pipeline请求，比如设置-P=50或更高)\n- Qedis的高性能有一部分得益于独立的网络线程处理IO，因此和redis比占了便宜。但Qedis逻辑仍然是单线程的。\n- 另一部分得益于C++ STL的高效率（CLANG的表现比GCC更好）。\n- 在测试前，你要确保std::list的size()是O(1)复杂度，这才遵循C++11的标准。否则list相关命令不可测。\n\n运行下面这个命令，试试和redis比一比~\n```bash\n./redis-benchmark -q -n 1000000 -P 50 -c 50\n```\n\n 我在rMBP late2013笔记本上测试结果如图：\n\n![image](https://github.com/loveyacper/Qedis/blob/master/performance.png)\n\n\n## 编写扩展模块\n Qedis支持动态库模块，可以在运行时添加新命令。\n 我添加了三个命令(ldel, skeys, hgets)作为演示。\n\n## 支持冷数据淘汰\n 是的，在内存受限的情况下，你可以让Qedis根据简单的LRU算法淘汰一些key以释放内存。\n\n## 主从复制，事务，RDB/AOF持久化，慢日志，发布订阅\n 这些特性Qedis都有:-)\n\n## 持久化：内存不再是上限\n Leveldb可以配置为Qedis的持久化存储引擎，可以存储更多的数据。\n\n\n## 命令列表\n#### 展示Qedis支持的所有命令，目前支持140个命令\n- cmdlist\n\n## TODO\n* 支持lua\n* Qedis Cluster多语言客户端\n\n"
  },
  {
    "path": "UnitTest/CMakeLists.txt",
    "content": "INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nAUX_SOURCE_DIRECTORY(. UNITTEST_SRC)\n\nLINK_DIRECTORIES(../../leveldb)\nLINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase)\n\nADD_EXECUTABLE(qunittest ${UNITTEST_SRC})\nSET(EXECUTABLE_OUTPUT_PATH  ../../bin)\nTARGET_LINK_LIBRARIES(qunittest  qediscore; leveldb)\nADD_DEPENDENCIES(qunittest qediscore)\n"
  },
  {
    "path": "UnitTest/QGlobRegex_unittest.cc",
    "content": "#include \"UnitTest.h\"\n#include \"QGlobRegex.h\"\n\nusing namespace qedis;\n\nTEST_CASE(regex_star)\n{\n    EXPECT_TRUE(glob_match(\"a*b?*?\", \"abxy\"));\n    EXPECT_TRUE(glob_match(\"*a\", \"aa\"));\n    EXPECT_TRUE(glob_match(\"*a\", \"abaa\"));\n    EXPECT_TRUE(glob_match(\"***a\", \"aaaa\"));\n    EXPECT_FALSE(glob_match(\"a*b?*?\", \"cb\"));\n\n    EXPECT_TRUE(glob_match(\"a*b\", \"ab\"));\n    EXPECT_TRUE(glob_match(\"a*b\", \"acb\"));\n    EXPECT_TRUE(glob_match(\"a*b\", \"acccb\"));\n    EXPECT_FALSE(glob_match(\"a*b\", \"accc\"));\n}\n\nTEST_CASE(regex_question)\n{\n    EXPECT_FALSE(glob_match(\"a?b\", \"ab\"));\n    EXPECT_TRUE(glob_match(\"a?b\", \"acb\"));\n    EXPECT_FALSE(glob_match(\"a?b\", \"acc\"));\n    EXPECT_FALSE(glob_match(\"a?b\", \"accb\"));\n}\n\nTEST_CASE(regex_bracket)\n{\n    EXPECT_TRUE(glob_match(\"[-]\", \"-\"));\n    EXPECT_TRUE(glob_match(\"[-abc]\", \"-\"));\n    EXPECT_TRUE(glob_match(\"[-]??\", \"--x\"));\n\n    EXPECT_TRUE(glob_match(\"[a-ce-a]\", \"a\"));\n\n    EXPECT_TRUE(glob_match(\"[a-eg-z]\", \"u\"));\n    EXPECT_FALSE(glob_match(\"[^a-eg-z]\", \"u\"));\n    EXPECT_TRUE(glob_match(\"[abcx-z]\", \"c\"));\n    EXPECT_TRUE(glob_match(\"[abcx-z]\", \"y\"));\n    EXPECT_FALSE(glob_match(\"[^abcx-z]\", \"c\"));\n    EXPECT_FALSE(glob_match(\"[^abcx-z]\", \"y\"));\n\n    EXPECT_FALSE(glob_match(\"[a-z]\", \"U\"));\n    EXPECT_TRUE(glob_match(\"[^a-z]\", \"U\"));\n\n    EXPECT_TRUE(glob_match(\"[a--]\", \"-\"));\n    EXPECT_FALSE(glob_match(\"[^a--]\", \"-\"));\n\n    EXPECT_TRUE(glob_match(\"[---]\", \"-\"));\n    EXPECT_TRUE(glob_match(\"[-]\", \"-\"));\n    EXPECT_TRUE(glob_match(\"[-xyz]\", \"-\"));\n    EXPECT_TRUE(glob_match(\"[]]\", \"]\"));\n    EXPECT_TRUE(glob_match(\"x[]abc]\", \"x]\"));\n    EXPECT_FALSE(glob_match(\"x[ab]c]\", \"x]\"));\n\n    EXPECT_TRUE(glob_match(\"x\\\\[ab]y\", \"x[ab]y\"));\n    EXPECT_TRUE(glob_match(\"x*\\\\\", \"xab\\\\\"));\n    EXPECT_TRUE(glob_match(\"\\\\*\\\\\", \"\\\\fuckyou\\\\\"));\n}\n\nTEST_CASE(regex_star_brackets)\n{\n    EXPECT_FALSE(glob_match(\"[a-e]\", \"xa\"));\n    EXPECT_FALSE(glob_match(\"*[a-e]*\", \"x\"));\n    EXPECT_FALSE(glob_match(\"*[a-e]*\", \"xy\"));\n    EXPECT_TRUE(glob_match(\"*[a-e]*\", \"xya\"));\n    EXPECT_TRUE(glob_match(\"*[a-e]*\", \"xay\"));\n    EXPECT_TRUE(glob_match(\"*[a-e]*\", \"axy\"));\n}\n\nTEST_CASE(regex_search)\n{\n    EXPECT_TRUE(glob_search(\"[a-e]\", \"xa\"));\n    EXPECT_FALSE(glob_search(\"[a-e]\", \"x\"));\n    EXPECT_FALSE(glob_search(\"[a-e]\", \"xy\"));\n    EXPECT_TRUE(glob_search(\"[a-e]\", \"xya\"));\n    EXPECT_TRUE(glob_search(\"[a-e]\", \"xay\"));\n    EXPECT_TRUE(glob_search(\"[a-e]\", \"axy\"));\n}\n\nTEST_CASE(regex_strange)\n{\n    EXPECT_FALSE(glob_match(\"[a-e\", \"a\"));\n    EXPECT_TRUE(glob_match(\"*a-e]*\", \"a-e]\"));\n    EXPECT_FALSE(glob_match(\"[a-e*\", \"a\"));\n    EXPECT_TRUE(glob_match(\"a[-]\", \"a-\"));\n    EXPECT_FALSE(glob_match(\"a\\[\", \"a[\"));\n    EXPECT_TRUE(glob_match(\"a\\\\[\", \"a[\"));\n    EXPECT_TRUE(glob_match(\"a[]]\", \"a]\"));\n\n    EXPECT_TRUE(glob_match(\"a[^^]\", \"a$\"));\n    EXPECT_FALSE(glob_match(\"a[^^]\", \"a^\"));\n\n    EXPECT_TRUE(glob_match(\"a**[^^]\", \"a^!\"));\n    EXPECT_FALSE(glob_match(\"**a**\", \"xyz\"));\n}\n\n"
  },
  {
    "path": "UnitTest/UnitTest.cc",
    "content": "#include \"UnitTest.h\"\n#include <iostream>\n\nenum Color\n{\n    Color_red       =  1,\n    Color_green         ,\n    Color_yellow        ,\n    Color_normal        ,\n    Color_blue          ,\n    Color_purple        ,\n    Color_white         ,\n    Color_max           ,\n};\n\nvoid SetColor(Color c)\n{\n    static const char* colors[Color_max] = {\n        \"\",\n        \"\\033[1;31;40m\",\n        \"\\033[1;32;40m\",\n        \"\\033[1;33;40m\",\n        \"\\033[0m\",\n        \"\\033[1;34;40m\",\n        \"\\033[1;35;40m\",\n        \"\\033[1;37;40m\",\n    };\n\n    fprintf(stdout, \"%s\", colors[c]);\n}\n\nUnitTestBase::UnitTestBase() : pass_(true), abort_(false)\n{\n    UnitTestManager::Instance().AddTest(this);\n}\n    \n    \nUnitTestBase& UnitTestBase::SetInfo(const std::string& exprInfo, bool pass, bool abort)\n{\n    pass_  = pass;\n    abort_ = abort;\n    expr_  = (pass_ ? \"[passed]: \" : \"[failed]: \") + exprInfo;\n    return *this;\n}\n\n\nvoid  UnitTestBase::Print() const\n{\n    SetColor(Color_red);\n\n    for (const auto& e : errors_)\n        std::cout << e << std::endl;\n\n    SetColor(Color_normal);\n}\n\n    \nvoid UnitTestBase::FlushError()\n{\n    if (pass_)\n    {\n        SetColor(Color_green);\n        std::cout << expr_ << std::endl;\n        return;\n    }\n\n    errors_.push_back(expr_);\n    if (abort_)\n    {\n        Print();\n        ::abort();\n    }\n}\n\n\n// test mgr\nUnitTestManager& UnitTestManager::Instance()\n{\n    static UnitTestManager  mgr;\n    return mgr;\n}\n\nvoid UnitTestManager::AddTest(UnitTestBase* test)\n{\n    tests_.push_back(test);\n}\n\nvoid UnitTestManager::Clear()\n{\n    tests_.clear();\n}\n\nvoid UnitTestManager::Run()\n{\n    std::size_t  pass = 0;\n    std::size_t  fail = 0;\n\n    for (const auto& ut : tests_)\n    {\n        ut->Run();\n        if (ut->IsFine())\n        {\n            ++ pass;\n\n            SetColor(Color_white);\n            std::cout << \"ALL PASSED! \" << ut->GetName() << std::endl;\n        }\n        else\n        {\n            ++ fail;\n\n            ut->Print();\n            SetColor(Color_purple);\n            std::cout << \"FAILED! \" << ut->GetName() << std::endl;\n        }\n    }\n\n    Clear();\n\n    SetColor(fail == 0 ? Color_blue: Color_yellow);\n    std::cout << (pass + fail) << \" cases: \"\n              << pass << \" passed, \"\n              << fail << \" failed\\n\";\n    SetColor(Color_normal);\n}\n\nint main()\n{\n    RUN_ALL_TESTS();\n    return 0;\n}\n\n"
  },
  {
    "path": "UnitTest/UnitTest.h",
    "content": "#ifndef BERT_UNITTEST_H\n#define BERT_UNITTEST_H\n\n#include <string>\n#include <sstream>\n#include <vector>\n\nclass UnitTestBase\n{\npublic:\n    friend class MsgHelper;\n\n    UnitTestBase();\n    // stack only, no need virtual destructor, but the warning...\n    virtual ~UnitTestBase() {}\n    \n    const std::string& GetName() const { return name_; }\n    \n    virtual void Run() = 0;\n\n    bool  IsFine() const { return errors_.empty(); }\n    void  Print()  const;\n\n    template <typename T>\n    UnitTestBase& operator << (const T &  t);\n\nprotected:\n    UnitTestBase& SetInfo(const std::string& exprInfo, bool pass = true, bool abort = false);\n    std::string name_;\n\nprivate:\n    void    FlushError();\n    \n    bool    pass_;\n    bool    abort_;\n\n    std::string     expr_;\n    std::vector<std::string>    errors_;\n    \nprivate:\n    UnitTestBase(const UnitTestBase& ) = delete;\n    UnitTestBase& operator= (const UnitTestBase& ) = delete;\n\n    void* operator new(std::size_t ); // stack only\n};\n\ntemplate <typename T>\ninline UnitTestBase& UnitTestBase::operator<< (const T &  t)\n{\n    if (!pass_)\n    {\n        std::ostringstream  str;\n        str << t;\n        expr_ += str.str();\n    }\n\n    return *this;\n}\n\nclass MsgHelper \n{\npublic:\n    void operator=(UnitTestBase& test)\n    {\n        test.FlushError();\n    }\n};\n\n\n\n#define   TEST_CASE(name)                           \\\n    class UnitTestBase##name: public UnitTestBase   \\\n    {                                               \\\n    public:                                         \\\n        UnitTestBase##name() {                      \\\n            name_ = #name;                         \\\n        }                                           \\\n        virtual void Run();                         \\\n    } test_##name##_obj;                            \\\n    void    UnitTestBase##name::Run()\n\n\n#define EXPECT_TRUE(expr)   \\\n    MsgHelper()=((expr) ? SetInfo(\"'\"#expr\"'\", true) : SetInfo(\"'\"#expr\"'\", false))\n\n#define EXPECT_FALSE(expr)  \\\n    EXPECT_TRUE(!(expr))\n\n#define ASSERT_TRUE(expr)   \\\n    MsgHelper()=((expr) ? SetInfo(\"'\"#expr\"'\", true) : SetInfo(\"'\"#expr\"' \", false, true))\n\n#define ASSERT_FALSE(expr)  \\\n    ASSERT_TRUE(!(expr))\n\n\n\nclass UnitTestManager\n{\npublic:\n    static  UnitTestManager&    Instance();\n\n    void    AddTest(UnitTestBase* test);\n    void    Clear();\n    void    Run();\nprivate:\n    UnitTestManager() {}\n    \n    std::vector<UnitTestBase* > tests_;\n};\n\n#define RUN_ALL_TESTS   UnitTestManager::Instance().Run\n\n#endif\n\n"
  },
  {
    "path": "cluster/.gitignore",
    "content": "# MAC OS\n.DS_Store\n\n#\nbuild/\nbin/\n"
  },
  {
    "path": "cluster/CMakeCommon",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nIF(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n    SET(CMAKE_CXX_FLAGS_DEBUG  \"-g -Wall -std=c++0x -lpthread\")\n    SET(CMAKE_CXX_FLAGS_RELEASE \"-O2 -Wall -std=c++0x -lpthread\")\nELSEIF(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n    SET(CMAKE_CXX_FLAGS_DEBUG  \"-g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions\")\n    SET(CMAKE_CXX_FLAGS_RELEASE \"-O2 -g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions\")\n    ADD_DEFINITIONS(-Dthread_local=__thread) \nELSE()\n    message(FATAL_ERROR \"Only support linux or OS X\")\nENDIF()\n\nOPTION(DEBUG \"Debug or release\" ON)\n\nIF(DEBUG)\n    SET(CMAKE_BUILD_TYPE \"Debug\")\nELSE()\n    SET(CMAKE_BUILD_TYPE \"Release\")\nENDIF()\n\n"
  },
  {
    "path": "cluster/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nPROJECT(QEDIS_CLUSTER)\n\nSUBDIRS(ananas)\n\nSET(USE_ZOOKEEPER 1)\n\nADD_DEFINITIONS(-DUSE_ZOOKEEPER=${USE_ZOOKEEPER})\n\nSUBDIRS(cluster_conn)\nSUBDIRS(qedis_proxy)\n"
  },
  {
    "path": "cluster/Makefile",
    "content": "all:\n\tmkdir -p build && cd build && cmake .. && make\nclean:\n\tcd build && make clean\n"
  },
  {
    "path": "cluster/README.md",
    "content": "# Qedis分布式集群\n\n## 环境需求\n* C++11、CMake\n* zookeeper\n* Linux 或 MAC OS\n\n## 代码目录\n\n* ananas\n\n  一个C++11编写的网络库，提供了强大的future异步编程模式.\n\n* cluster_conn\n\n  针对zookeeper或etcd的包装。目前只提供了zookeeper.\n\n* qedis_proxy\n\n  Qedis代理服务器，负责发现Qedis服务，转发客户端请求和Qedis服务器响应.\n \n## Future模式\n  本目录代码采用了基于Future模式的异步编程,例如与zookeeper集群连接时,需要进行7个步骤:\n\n\n  * 握手\n  * 注册自己，同时获取Qedis set信息,这是两个并行的异步请求\n  * 根据set信息，获取分片信息\n  * 存储分片信息\n  * 发起异步请求:获取Qedis服务列表\n  * 存储Qedis服务信息\n  * 初始化ping定时器\n  * 如若以上操作任一无响应，触发超时逻辑\n  这一连串的操作使用future模式编写如下,每一个Then都是前面异步请求的回调:\n  ```cpp\nctx_->DoHandshake()\n    .Then([me = this](const ZkResponse& rsp) mutable {\n        return me->_ProcessHandshake(rsp);\n    })\n    .Then([me = this](ananas::Try<qedis::ZookeeperContext* >&& tctx) mutable {\n        return me->_RegisterAndGetServers(std::move(tctx));\n    })\n    .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& rsps) mutable {\n        return me->_GetShardingInfo(rsps);\n    })\n    .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& vrsp) mutable {\n        if (!me->_ProcessShardingInfo(vrsp)) {\n            using InnerType = std::vector<ananas::Try<ZkResponse>>;\n            auto exp = std::runtime_error(\"ProcessShardingInfo failed\");\n            return ananas::MakeExceptionFuture<InnerType>(exp);\n        }\n\n        // 5. get qedis server's list and watch the qedis server list\n        return me->_GetServers(vrsp);\n    })\n    .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& vrsp) mutable {\n        return me->_ProcessServerInfo(vrsp);\n    })\n    .Then([me = this](bool succ) {\n        if (succ)\n            me->_InitPingTimer();\n    })\n    .OnTimeout(std::chrono::seconds(3), []() {\n            // 3秒钟超时\n            std::cout << \"OnTimeout handshake\\n\";\n            ananas::EventLoop::ExitApplication();\n        }, conn_->GetLoop()\n    );\n  ```\n\n## 集群特性\n 待写，代码实现中。。。\n\n"
  },
  {
    "path": "cluster/ananas/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nSUBDIRS(net)\n"
  },
  {
    "path": "cluster/ananas/future/Future.h",
    "content": "#ifndef BERT_FUTURE_H\r\n#define BERT_FUTURE_H\r\n\r\n#include <atomic>\r\n#include <mutex>\r\n#include <functional>\r\n#include <type_traits>\r\n\r\n#include \"Helper.h\"\r\n#include \"Try.h\"\r\n#include \"util/Scheduler.h\"\r\n\r\nnamespace ananas\r\n{\r\n\r\nnamespace internal\r\n{\r\n\r\nenum class Progress\r\n{\r\n    None,\r\n    Timeout,\r\n    Done\r\n};\r\n\r\nusing TimeoutCallback = std::function<void ()>;\r\n\r\ntemplate <typename T>\r\nstruct State\r\n{\r\n    static_assert(std::is_same<T, void>::value || std::is_copy_constructible<T>(),\r\n                  \"must be copyable or void\");\r\n    static_assert(std::is_same<T, void>::value || std::is_move_constructible<T>(),\r\n                  \"must be movable or void\");\r\n\r\n    State() :\r\n        progress_(Progress::None),\r\n        retrieved_ {false}\r\n    {\r\n    }\r\n\r\n    std::mutex thenLock_;\r\n\r\n    Try<T> value_;\r\n    std::function<void (Try<T>&& )> then_;\r\n    Progress progress_;\r\n\r\n    std::function<void (TimeoutCallback&& )> onTimeout_;\r\n    std::atomic<bool> retrieved_;\r\n\r\n    bool IsRoot() const { return !onTimeout_; }\r\n};\r\n\r\n} // end namespace internal\r\n\r\n\r\ntemplate <typename T>\r\nclass Future;\r\n\r\ntemplate <typename T>\r\nclass Promise\r\n{\r\npublic:\r\n    Promise() :\r\n        state_(std::make_shared<internal::State<T>>())\r\n    {\r\n    }\r\n\r\n    // TODO: C++11 lambda doesn't support move capture\r\n    // just for compile, copy Promise is undefined, do NOT do that!\r\n    Promise(const Promise&) = default;\r\n    Promise& operator= (const Promise&) = default;\r\n\r\n    Promise(Promise&& pm) = default;\r\n    Promise& operator= (Promise&& pm) = default;\r\n\r\n    void SetException(std::exception_ptr exp)\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = Try<T>(std::move(exp));\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<!std::is_void<SHIT>::value, void>::type\r\n    SetValue(SHIT&& t)\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = std::move(t);\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<!std::is_void<SHIT>::value, void>::type\r\n    SetValue(const SHIT& t)\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = t;\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<!std::is_void<SHIT>::value, void>::type\r\n    SetValue(Try<SHIT>&& t)\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = std::move(t);\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<!std::is_void<SHIT>::value, void>::type\r\n    SetValue(const Try<SHIT>& t)\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = t;\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<std::is_void<SHIT>::value, void>::type\r\n    SetValue(Try<void>&& )\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = Try<void>();\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<std::is_void<SHIT>::value, void>::type\r\n    SetValue(const Try<void>& )\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = Try<void>();\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    template <typename SHIT = T>\r\n    typename std::enable_if<std::is_void<SHIT>::value, void>::type\r\n    SetValue()\r\n    {\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        bool isRoot = state_->IsRoot();\r\n        if (isRoot && state_->progress_ != internal::Progress::None)\r\n            return;\r\n\r\n        state_->progress_ = internal::Progress::Done;\r\n\r\n        state_->value_ = Try<void>();\r\n        if (state_->then_)\r\n            state_->then_(std::move(state_->value_));\r\n    }\r\n\r\n    Future<T> GetFuture()\r\n    {\r\n        bool expect = false;\r\n        if (!state_->retrieved_.compare_exchange_strong(expect, true))\r\n        {\r\n            struct FutureAlreadyRetrieved {};\r\n            throw FutureAlreadyRetrieved();\r\n        }\r\n\r\n        return Future<T>(state_);\r\n    }\r\n\r\nprivate:\r\n    std::shared_ptr<internal::State<T>> state_;\r\n};\r\n\r\n\r\ntemplate <typename T>\r\nclass Future\r\n{\r\npublic: \r\n    using InnerType = T;\r\n\r\n    template <typename U>\r\n    friend class Future;\r\n\r\n    Future()\r\n    {\r\n    }\r\n\r\n    Future(const Future&) = delete;\r\n    void operator= (const Future&) = delete;\r\n\r\n    Future(Future&& fut) = default;\r\n    Future& operator= (Future&& fut) = default;\r\n\r\n    explicit\r\n    Future(std::shared_ptr<internal::State<T>> state) :\r\n        state_(std::move(state))\r\n    {\r\n    }\r\n\r\n    template <typename F,\r\n              typename R = internal::CallableResult<F, T> > \r\n    auto Then(F&& f) -> typename R::ReturnFutureType \r\n    {\r\n        typedef typename R::Arg Arguments;\r\n        return _ThenImpl<F, R>(nullptr, std::forward<F>(f), Arguments());  \r\n    }\r\n\r\n    // f will be called in sched\r\n    template <typename F,\r\n              typename R = internal::CallableResult<F, T> > \r\n    auto Then(Scheduler* sched, F&& f) -> typename R::ReturnFutureType \r\n    {\r\n        typedef typename R::Arg Arguments;\r\n        return _ThenImpl<F, R>(sched, std::forward<F>(f), Arguments());  \r\n    }\r\n\r\n    //1. F does not return future type\r\n    template <typename F, typename R, typename... Args>\r\n    typename std::enable_if<!R::IsReturnsFuture::value, typename R::ReturnFutureType>::type\r\n    _ThenImpl(Scheduler* sched, F&& f, internal::ResultOfWrapper<F, Args...> )\r\n    {\r\n        static_assert(std::is_void<T>::value ? sizeof...(Args) == 0 : sizeof...(Args) == 1,\r\n                      \"Then callback must take 0/1 argument\");\r\n\r\n        using FReturnType = typename R::IsReturnsFuture::Inner;\r\n        using namespace internal;\r\n\r\n        Promise<FReturnType> pm;\r\n        auto nextFuture = pm.GetFuture();\r\n\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        if (state_->progress_ == Progress::Timeout)\r\n        {\r\n            struct FutureWrongState {};\r\n            throw FutureWrongState();\r\n        }\r\n        else if (state_->progress_ == Progress::Done)\r\n        {\r\n            Try<T> t;\r\n            try {\r\n                t = std::move(state_->value_);\r\n            }\r\n            catch(const std::exception& e) {\r\n                t = Try<T>(std::current_exception());\r\n            }\r\n\r\n            guard.unlock();\r\n\r\n            auto func = [res = std::move(t),\r\n                         f = std::move((typename std::decay<F>::type)f),\r\n                         prom = std::move(pm)]() mutable {\r\n                auto result = WrapWithTry(f, std::move(res));\r\n                prom.SetValue(std::move(result));\r\n            };\r\n\r\n            if (sched)\r\n                sched->ScheduleOnce(std::move(func));\r\n            else\r\n                func();\r\n        }\r\n        else\r\n        {\r\n            // 1. set pm's timeout callback\r\n            nextFuture.SetOnTimeout([weak_parent = std::weak_ptr<State<T>>(state_)](TimeoutCallback&& cb) {\r\n                    auto parent = weak_parent.lock();\r\n                    if (!parent)\r\n                        return;\r\n\r\n                    {\r\n                        std::unique_lock<std::mutex> guard(parent->thenLock_);\r\n                        if (parent->progress_ != Progress::None)\r\n                            return;\r\n                    \r\n                        parent->progress_ = Progress::Timeout;\r\n                    }\r\n\r\n                    if (!parent->IsRoot())\r\n                        parent->onTimeout_(std::move(cb)); // propogate to the root\r\n                    else\r\n                        cb();\r\n                });\r\n\r\n            // 2. set this future's then callback\r\n            SetCallback([sched,\r\n                         func = std::move((typename std::decay<F>::type)f),\r\n                         prom = std::move(pm)](Try<T>&& t) mutable {\r\n\r\n                auto cb = [func = std::move(func), t = std::move(t), prom = std::move(prom)]() mutable {\r\n                    // run callback, T can be void, thanks to folly Try<>\r\n                    auto result = WrapWithTry(func, std::move(t));\r\n                    // set next future's result\r\n                    prom.SetValue(std::move(result));\r\n                };\r\n\r\n                if (sched)\r\n                    sched->ScheduleOnce(std::move(cb));\r\n                else\r\n                    cb();\r\n            });\r\n        }\r\n\r\n        return std::move(nextFuture);\r\n    }\r\n\r\n    //2. F return another future type\r\n    template <typename F, typename R, typename... Args>\r\n    typename std::enable_if<R::IsReturnsFuture::value, typename R::ReturnFutureType>::type\r\n    _ThenImpl(Scheduler* sched, F&& f, internal::ResultOfWrapper<F, Args...>)\r\n    {\r\n        static_assert(sizeof...(Args) <= 1, \"Then must take zero/one argument\");\r\n\r\n        using FReturnType = typename R::IsReturnsFuture::Inner;\r\n        using namespace internal;\r\n\r\n        Promise<FReturnType> pm;\r\n        auto nextFuture = pm.GetFuture();\r\n\r\n        std::unique_lock<std::mutex> guard(state_->thenLock_);\r\n        if (state_->progress_ == Progress::Timeout)\r\n        {\r\n            struct FutureWrongState {};\r\n            throw FutureWrongState();\r\n        }\r\n        else if (state_->progress_ == Progress::Done)\r\n        {\r\n            Try<T> t;\r\n            try {\r\n                t = std::move(state_->value_);\r\n            }\r\n            catch(const std::exception& e) {\r\n                t = Try<T>(std::current_exception());\r\n            }\r\n\r\n            guard.unlock();\r\n\r\n            auto cb = [res = std::move(t),\r\n                       f = std::move((typename std::decay<F>::type)f),\r\n                       prom = std::move(pm)]() mutable {\r\n                // because func return another future: innerFuture, when innerFuture is done, nextFuture can be done\r\n                auto innerFuture = f(res.template Get<Args>()...);\r\n                std::unique_lock<std::mutex> guard(innerFuture.state_->thenLock_);\r\n                if (innerFuture.state_->progress_ == Progress::Timeout) {\r\n                    struct FutureWrongState {};\r\n                    throw FutureWrongState();\r\n                }\r\n                else if (innerFuture.state_->progress_ == Progress::Done) {\r\n                    Try<FReturnType> t;\r\n                    try {\r\n                        t = std::move(innerFuture.state_->value_);\r\n                    }\r\n                    catch(const std::exception& e) {\r\n                        t = Try<FReturnType>(std::current_exception());\r\n                    }\r\n\r\n                    guard.unlock();\r\n                    prom.SetValue(std::move(t));\r\n                }\r\n                else {\r\n                    innerFuture.SetCallback([prom = std::move(prom)](Try<FReturnType>&& t) mutable {\r\n                        prom.SetValue(std::move(t));\r\n                    });\r\n                }\r\n            };\r\n\r\n            if (sched)\r\n                sched->ScheduleOnce(std::move(cb));\r\n            else\r\n                cb();\r\n        }\r\n        else\r\n        {\r\n            // 1. set pm's timeout callback\r\n            nextFuture.SetOnTimeout([weak_parent = std::weak_ptr<State<T>>(state_)](TimeoutCallback&& cb) {\r\n                    auto parent = weak_parent.lock();\r\n                    if (!parent)\r\n                        return;\r\n\r\n                    {\r\n                        std::unique_lock<std::mutex> guard(parent->thenLock_);\r\n                        if (parent->progress_ != Progress::None)\r\n                            return;\r\n                    \r\n                        parent->progress_ = Progress::Timeout;\r\n                    }\r\n\r\n                    if (!parent->IsRoot())\r\n                        parent->onTimeout_(std::move(cb)); // propogate to the root\r\n                    else\r\n                        cb();\r\n\r\n                });\r\n\r\n            // 2. set this future's then callback\r\n            SetCallback([sched = sched,\r\n                         func = std::move((typename std::decay<F>::type)f),\r\n                         prom = std::move(pm)](Try<T>&& t) mutable {\r\n                auto cb = [func = std::move(func), t = std::move(t), prom = std::move(prom)]() mutable {\r\n                    // because func return another future: innerFuture, when innerFuture is done, nextFuture can be done\r\n                    auto innerFuture = func(t.template Get<Args>()...);\r\n\r\n                    std::unique_lock<std::mutex> guard(innerFuture.state_->thenLock_);\r\n                    if (innerFuture.state_->progress_ == Progress::Timeout) {\r\n                        struct FutureWrongState {};\r\n                        throw FutureWrongState();\r\n                    }\r\n                    else if (innerFuture.state_->progress_ == Progress::Done) {\r\n                        Try<FReturnType> t;\r\n                        try {\r\n                            t = std::move(innerFuture.state_->value_);\r\n                        }\r\n                        catch(const std::exception& e) {\r\n                            t = Try<FReturnType>(std::current_exception());\r\n                        }\r\n\r\n                        guard.unlock();\r\n                        prom.SetValue(std::move(t));\r\n                    }\r\n                    else {\r\n                        innerFuture.SetCallback([prom = std::move(prom)](Try<FReturnType>&& t) mutable {\r\n                            prom.SetValue(std::move(t));\r\n                        });\r\n                    }\r\n                };\r\n\r\n                if (sched)\r\n                    sched->ScheduleOnce(std::move(cb));\r\n                else\r\n                    cb();\r\n            }); \r\n        }\r\n\r\n        return std::move(nextFuture);\r\n    }\r\n\r\n    void SetCallback(std::function<void (Try<T>&& )>&& func)\r\n    {\r\n        state_->then_ = std::move(func);\r\n    }\r\n\r\n    void SetOnTimeout(std::function<void (internal::TimeoutCallback&& )>&& func)\r\n    {\r\n        state_->onTimeout_ = std::move(func);\r\n    }\r\n\r\n    /*\r\n     * When register callbacks and timeout for a future like this:\r\n     *\r\n     *      Future<int> f;\r\n     *      f.Then(xx).Then(yy).OnTimeout(zz);\r\n     *\r\n     * There will be 3 future objects created except f, we call f as root future.\r\n     * The zz callback is registed on the last future, however, timeout and future satisfication\r\n     * can happened almost in the same time, we should ensure that both xx and yy will be called\r\n     * or zz will be called, but they can't happened both or neither. So we pass the cb\r\n     * to the root future, if we find out that root future is indeed timeout, we call cb there.\r\n     */\r\n    void OnTimeout(std::chrono::milliseconds duration,\r\n                   internal::TimeoutCallback f,\r\n                   Scheduler* scheduler)\r\n    {\r\n        scheduler->ScheduleOnceAfter(duration, [state = state_, cb = std::move(f)]() mutable {\r\n                {\r\n                    std::unique_lock<std::mutex> guard(state->thenLock_);\r\n\r\n                    if (state->progress_ != internal::Progress::None)\r\n                        return;\r\n\r\n                    state->progress_ = internal::Progress::Timeout;\r\n                }\r\n\r\n                if (!state->IsRoot())\r\n                    state->onTimeout_(std::move(cb)); // propogate to the root future\r\n                else\r\n                    cb();\r\n        });\r\n    }\r\n\r\nprivate:\r\n    std::shared_ptr<internal::State<T>> state_;\r\n};\r\n\r\n// Make ready future\r\ntemplate <typename T2>\r\ninline Future<T2> MakeReadyFuture(T2&& value)\r\n{\r\n    Promise<T2> pm;\r\n    auto f(pm.GetFuture());\r\n    pm.SetValue(std::forward<T2>(value));\r\n\r\n    return f;\r\n}\r\n\r\ntemplate <typename T2>\r\ninline Future<T2> MakeReadyFuture(const T2& value)\r\n{\r\n    Promise<T2> pm;\r\n    auto f(pm.GetFuture());\r\n    pm.SetValue(value);\r\n\r\n    return f;\r\n}\r\n\r\ninline Future<void> MakeReadyFuture()\r\n{\r\n    Promise<void> pm;\r\n    auto f(pm.GetFuture());\r\n    pm.SetValue();\r\n\r\n    return f;\r\n}\r\n\r\n// Make exception future\r\ntemplate <typename T2>\r\ninline Future<T2> MakeExceptionFuture(const std::exception& exp)\r\n{\r\n    Promise<T2> pm;\r\n    auto f(pm.GetFuture());\r\n    pm.SetException(std::make_exception_ptr(exp));\r\n\r\n    return f;\r\n}\r\n\r\n// When All\r\ntemplate <typename... FT>\r\ntypename internal::CollectAllVariadicContext<typename std::decay<FT>::type::InnerType...>::FutureType\r\nWhenAll(FT&&... futures)\r\n{\r\n    auto ctx = std::make_shared<internal::CollectAllVariadicContext<typename std::decay<FT>::type::InnerType...>>();\r\n\r\n    internal::CollectVariadicHelper<internal::CollectAllVariadicContext>( \r\n            ctx, std::forward<typename std::decay<FT>::type>(futures)...);\r\n\r\n    return ctx->pm.GetFuture();\r\n}\r\n\r\n\r\ntemplate <class InputIterator>\r\nFuture<\r\n    std::vector<\r\n    Try<typename std::iterator_traits<InputIterator>::value_type::InnerType>>>\r\n    WhenAll(InputIterator first, InputIterator last)\r\n{\r\n    using T = typename std::iterator_traits<InputIterator>::value_type::InnerType;\r\n    if (first == last)\r\n        return MakeReadyFuture(std::vector<Try<T>>());\r\n\r\n    struct CollectAllContext\r\n    {\r\n        CollectAllContext(int n) : results(n) {}\r\n        ~CollectAllContext()\r\n        { \r\n            // I think this line is useless.\r\n            // pm.SetValue(std::move(results));\r\n        }\r\n             \r\n        Promise<std::vector<Try<T>>> pm;\r\n        std::vector<Try<T>> results;\r\n        std::atomic<size_t> collected{0};\r\n    };\r\n\r\n    auto ctx = std::make_shared<CollectAllContext>(std::distance(first, last));\r\n                \r\n    for (size_t i = 0; first != last; ++first, ++i)\r\n    {\r\n        first->SetCallback([ctx, i](Try<T>&& t) {\r\n                ctx->results[i] = std::move(t);\r\n                if (ctx->results.size() - 1 ==\r\n                    std::atomic_fetch_add (&ctx->collected, std::size_t(1))) {\r\n                    ctx->pm.SetValue(std::move(ctx->results));\r\n                }\r\n            });\r\n    }\r\n\r\n    return ctx->pm.GetFuture();\r\n}\r\n\r\n// When Any\r\ntemplate <class InputIterator>\r\nFuture<\r\n  std::pair<size_t,\r\n           Try<typename std::iterator_traits<InputIterator>::value_type::InnerType>>>\r\nWhenAny(InputIterator first, InputIterator last)\r\n{\r\n    using T = typename std::iterator_traits<InputIterator>::value_type::InnerType;\r\n\r\n    if (first == last)\r\n    {\r\n        return MakeReadyFuture(std::make_pair(size_t(0), Try<T>(T())));\r\n    }\r\n\r\n    struct CollectAnyContext\r\n    {\r\n        CollectAnyContext() {};\r\n        Promise<std::pair<size_t, Try<T>>> pm;\r\n        std::atomic<bool> done{false};\r\n    };\r\n\r\n    auto ctx = std::make_shared<CollectAnyContext>();\r\n    for (size_t i = 0; first != last; ++first, ++i)\r\n    {\r\n        first->SetCallback([ctx, i](Try<T>&& t) {\r\n            if (!ctx->done.exchange(true)) {\r\n                ctx->pm.SetValue(std::make_pair(i, std::move(t)));\r\n            }\r\n       });\r\n    }\r\n           \r\n    return ctx->pm.GetFuture();\r\n}\r\n\r\n\r\n// When N \r\ntemplate <class InputIterator>\r\nFuture<\r\n    std::vector<\r\n    std::pair<size_t, Try<typename std::iterator_traits<InputIterator>::value_type::InnerType>>\r\n    >\r\n    >\r\n    WhenN(size_t N, InputIterator first, InputIterator last)\r\n{\r\n    using T = typename std::iterator_traits<InputIterator>::value_type::InnerType;\r\n\r\n    size_t nFutures = std::distance(first, last);\r\n    const size_t needCollect = std::min(nFutures, N);\r\n\r\n    if (needCollect == 0)\r\n    {\r\n        return MakeReadyFuture(std::vector<std::pair<size_t, Try<T>>>());\r\n    }\r\n\r\n    struct CollectNContext\r\n    {\r\n        CollectNContext(size_t _needs) : needs(_needs) {}\r\n        Promise<std::vector<std::pair<size_t, Try<T>>>> pm;\r\n    \r\n        std::mutex mutex;\r\n        std::vector<std::pair<size_t, Try<T>>> results;\r\n        const size_t needs;\r\n        bool done {false};\r\n    };\r\n\r\n    auto ctx = std::make_shared<CollectNContext>(needCollect);\r\n    for (size_t i = 0; first != last; ++first, ++i)\r\n    {\r\n        first->SetCallback([ctx, i](Try<T>&& t) {\r\n                std::unique_lock<std::mutex> guard(ctx->mutex);\r\n                if (ctx->done)\r\n                    return;\r\n                    \r\n                ctx->results.push_back(std::make_pair(i, std::move(t)));\r\n                if (ctx->needs == ctx->results.size()) {\r\n                    ctx->done = true;\r\n                    ctx->pm.SetValue(std::move(ctx->results));\r\n                }\r\n            });\r\n    }\r\n           \r\n    return ctx->pm.GetFuture();\r\n}\r\n\r\n} // end namespace ananas\r\n\r\n#endif\r\n\r\n"
  },
  {
    "path": "cluster/ananas/future/Helper.h",
    "content": "#ifndef BERT_HELPER_H\n#define BERT_HELPER_H\n\n/*\n * This file is modified from facebook folly, with my annotation\n */\n\n/*\n * Copyright 2016 Facebook, Inc.\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#include <tuple>\n#include <vector>\n#include <memory>\n\nnamespace ananas\n{\n\ntemplate <typename T>\nclass Future;\n\ntemplate <typename T>\nclass Promise;\n\ntemplate <typename T>\nclass Try;\n\nnamespace internal\n{\n\ntemplate<typename F, typename... Args>\nusing ResultOf = decltype(std::declval<F>()(std::declval<Args>()...));\n\n// I don't know why, but must do it to cater compiler...\ntemplate <typename F, typename... Args>                                   \nstruct ResultOfWrapper\n{\n    using Type = ResultOf<F, Args...>;\n};\n\n// Test if F can be called with Args type\ntemplate<typename F, typename... Args>        \nstruct CanCallWith\n{\n    // SFINAE  Check\n    template<typename T,\n        typename Dummy = ResultOf<T, Args...>>\n        static constexpr std::true_type\n        Check(std::nullptr_t dummy)\n        {\n            return std::true_type{};\n        };  \n                         \n    template<typename Dummy>\n        static constexpr std::false_type\n        Check(...)\n        {\n            return std::false_type{};\n        };\n                                \n    typedef decltype(Check<F>(nullptr)) type; // true_type if T可以接受Args作为参数        \n    static constexpr bool value = type::value; // the integral_constant's value            \n};              \n\n// simple traits\ntemplate <typename T>\nstruct IsFuture : std::false_type\n{ \n    using Inner = T;\n};\n\ntemplate <typename T> \nstruct IsFuture<Future<T>> : std::true_type\n{ \n    typedef T Inner; \n};\n\ntemplate<typename F, typename T>\nstruct CallableResult\n{ \n    // Test F call with arg type: void, T&&, T&, but do Not choose Try type as args\n    typedef\n        typename std::conditional<\n                 CanCallWith<F>::value, // if true, F can call with void\n                 ResultOfWrapper<F>,\n                 typename std::conditional< // NO, F(void) is invalid\n                          CanCallWith<F, T&&>::value, // if true, F(T&&) is valid\n                          ResultOfWrapper<F, T&&>, // Yes, F(T&&) is ok\n                          ResultOfWrapper<F, T&> >::type>::type Arg;  // Resort to F(T&)\n\n    // If ReturnsFuture::value is true, F returns another future type.\n    typedef IsFuture<typename Arg::Type> IsReturnsFuture; \n\n    // Future callback's result must be wrapped in another future\n    typedef Future<typename IsReturnsFuture::Inner> ReturnFutureType;\n};\n\n\n// CallableResult specilization for void.\n// I don't know why folly works without this...\ntemplate<typename F>\nstruct CallableResult<F, void>\n{ \n    // Test F call with arg type: void\n    typedef ResultOfWrapper<F> Arg;\n\n    // If ReturnsFuture::value is true, F returns another future type.\n    typedef IsFuture<typename Arg::Type> IsReturnsFuture; \n\n    // Future callback's result must be wrapped in another future\n    typedef Future<typename IsReturnsFuture::Inner> ReturnFutureType;\n};\n\n// For when_all\n//\ntemplate <typename... ELEM> \nstruct CollectAllVariadicContext\n{\n    CollectAllVariadicContext() {}\n   \n    // Differ from folly: Do nothing here\n    ~CollectAllVariadicContext() { }\n\n    CollectAllVariadicContext(const CollectAllVariadicContext& ) = delete;\n    void operator= (const CollectAllVariadicContext& ) = delete;\n    \n    template <typename T, size_t I>\n    inline void SetPartialResult(Try<T>& t)\n    {\n        std::get<I>(results) = std::move(t);\n        collects.push_back(I);\n        if (collects.size() == std::tuple_size<decltype(results)>::value)\n            pm.SetValue(std::move(results));\n    }\n        \n    \n    Promise<std::tuple<Try<ELEM>...>> pm;\n    std::tuple<Try<ELEM>...> results;\n    std::vector<size_t> collects;\n    \n    typedef Future<std::tuple<Try<ELEM>...>> FutureType;\n};\n\n// base template\ntemplate <template <typename...> class CTX, typename... Ts>\nvoid CollectVariadicHelper(const std::shared_ptr<CTX<Ts...>>& )\n{\n}\n      \ntemplate <template <typename ...> class CTX, typename... Ts,\n         typename THead, typename... TTail> \nvoid CollectVariadicHelper(const std::shared_ptr<CTX<Ts...>>& ctx, \n     THead&& head, TTail&&... tail)\n{\n    head.SetCallback([ctx](Try<typename THead::InnerType>&& t) { \n         ctx->template SetPartialResult<typename THead::InnerType, \n         sizeof...(Ts) - sizeof...(TTail) - 1>(t); \n         }); \n\n    CollectVariadicHelper(ctx, std::forward<TTail>(tail)...); \n}\n\n\n} // end namespace internal\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/future/Try.h",
    "content": "#ifndef BERT_TRY_H\r\n#define BERT_TRY_H\r\n\r\n/*\r\n * This class is modified from facebook folly\r\n * Thanks to it for handling the nasty void thing! \r\n */\r\n\r\n/*\r\n * Copyright 2016 Facebook, Inc.\r\n *  \r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n#include <exception>\r\n#include <stdexcept>\r\n#include <cassert>\r\n\r\nnamespace ananas\r\n{\r\n\r\ntemplate <typename T>\r\nclass Try\r\n{\r\n    enum class State\r\n    {\r\n        None,\r\n        Exception,\r\n        Value,\r\n    };\r\npublic:\r\n    Try() : state_(State::None)\r\n    {\r\n    }\r\n\r\n    explicit Try(const T& t) :\r\n        state_(State::Value),\r\n        value_(t)\r\n    {\r\n    }\r\n\r\n    Try(T&& t) :\r\n        state_(State::Value),\r\n        value_(std::move(t))\r\n    {\r\n    }\r\n\r\n    explicit Try(std::exception_ptr e) :\r\n        state_(State::Exception),\r\n        exception_(std::move(e))\r\n    {\r\n    }\r\n\r\n    // move\r\n    Try(Try<T>&& t) :\r\n        state_(t.state_)\r\n    {\r\n        t.state_ = State::None;\r\n        if (state_ == State::Value)\r\n            new (&value_)T(std::move(t.value_));\r\n        else if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(std::move(t.exception_));\r\n    }\r\n\r\n    Try<T>& operator=(Try<T>&& t)\r\n    {\r\n        if (this == &t)\r\n            return *this;\r\n\r\n        this->~Try();\r\n\r\n        state_ = t.state_;\r\n        t.state_ = State::None;\r\n        if (state_ == State::Value)\r\n            new (&value_)T(std::move(t.value_));\r\n        else if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(std::move(t.exception_));\r\n\r\n        return *this;\r\n    } \r\n\r\n    // copy\r\n    Try(const Try<T>& t) :\r\n        state_(t.state_)\r\n    {\r\n        if (state_ == State::Value)\r\n            new (&value_)T(t.value_);\r\n        else if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(t.exception_);\r\n    }\r\n\r\n    Try<T>& operator=(const Try<T>& t)\r\n    {\r\n        if (this == &t)\r\n            return *this;\r\n\r\n        this->~Try();\r\n\r\n        state_ = t.state_;\r\n        if (state_ == State::Value)\r\n            new (&value_)T(t.value_);\r\n        else if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(t.exception_);\r\n\r\n        return *this;\r\n    }\r\n\r\n    ~Try()\r\n    {\r\n        if (state_ == State::Exception)\r\n            exception_.~exception_ptr();\r\n        else if (state_ == State::Value)\r\n            value_.~T();\r\n    }\r\n\r\n    // implicity convertion\r\n    operator const T& () const & { return Value(); }\r\n    operator T& () & { return Value(); }\r\n    operator T&& () && { return std::move(Value()); }\r\n\r\n    // get value\r\n    const T& Value() const &\r\n    {\r\n        Check();\r\n        return value_;\r\n    }\r\n\r\n    T& Value() & \r\n    {\r\n        Check();\r\n        return value_;\r\n    }\r\n\r\n    T&& Value() &&\r\n    {\r\n        Check();\r\n        return std::move(value_);\r\n    }\r\n\r\n    // get exception\r\n    const std::exception_ptr& Exception() const &\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return exception_;\r\n    }\r\n\r\n    std::exception_ptr& Exception() &\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return exception_;\r\n    }\r\n\r\n    std::exception_ptr&& Exception() &&\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return std::move(exception_);\r\n    }\r\n\r\n    bool HasValue() const { return state_ == State::Value; }\r\n    bool HasException() const { return state_ == State::Exception; }\r\n\r\n    const T& operator*() const { return Value(); }\r\n    T& operator*() { return Value(); }\r\n    \r\n    struct UninitializedTry {};\r\n    \r\n    void Check() const \r\n    {\r\n        if (state_ == State::Exception)\r\n            std::rethrow_exception(exception_);\r\n        else if (state_ == State::None)\r\n            throw UninitializedTry();\r\n    }\r\n\r\n    // Amazing! Thanks to folly\r\n    template <typename R>\r\n    R Get() { return std::forward<R>(Value()); }\r\n\r\nprivate:\r\n    State state_;\r\n    union\r\n    {\r\n        T value_;\r\n        std::exception_ptr exception_;\r\n    };\r\n};\r\n\r\n\r\ntemplate <>\r\nclass Try<void>\r\n{\r\n    enum class State\r\n    {\r\n        Exception,\r\n        Value,\r\n    };\r\n\r\npublic:\r\n    Try() :\r\n        state_(State::Value)\r\n    {\r\n    }\r\n\r\n    explicit Try(std::exception_ptr e) :\r\n        state_(State::Exception),\r\n        exception_(std::move(e))\r\n    {\r\n    }\r\n\r\n    // move\r\n    Try(Try<void>&& t) :\r\n        state_(t.state_)\r\n    {\r\n        if (state_ == State::Exception)\r\n        {\r\n            new (&exception_)std::exception_ptr(std::move(t.exception_));\r\n            t.state_ = State::Value;\r\n        }\r\n    }\r\n\r\n    Try<void>& operator=(Try<void>&& t)\r\n    {\r\n        if (this == &t)\r\n            return *this;\r\n\r\n        this->~Try();\r\n\r\n        state_ = t.state_;\r\n        if (state_ == State::Exception)\r\n        {\r\n            new (&exception_)std::exception_ptr(std::move(t.exception_));\r\n            t.state_ = State::Value;\r\n        }\r\n\r\n        return *this;\r\n    }\r\n\r\n    // copy\r\n    Try(const Try<void>& t) :\r\n        state_(t.state_)\r\n    {\r\n        if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(t.exception_);\r\n    }\r\n\r\n    Try<void>& operator=(const Try<void>& t)\r\n    {\r\n        if (this == &t)\r\n            return *this;\r\n\r\n        this->~Try();\r\n\r\n        state_ = t.state_;\r\n        if (state_ == State::Exception)\r\n            new (&exception_)std::exception_ptr(t.exception_);\r\n\r\n        return *this;\r\n    }\r\n\r\n    ~Try()\r\n    {\r\n        if (state_ == State::Exception)\r\n            exception_.~exception_ptr();\r\n    }\r\n\r\n    // get exception\r\n    const std::exception_ptr& Exception() const &\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return exception_;\r\n    }\r\n\r\n    std::exception_ptr& Exception() &\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return exception_;\r\n    }\r\n\r\n    std::exception_ptr&& Exception() &&\r\n    {\r\n        if (!HasException())\r\n            throw std::runtime_error(\"Not exception state\");\r\n\r\n        return std::move(exception_);\r\n    }\r\n   \r\n    bool HasValue() const { return state_ == State::Value; }\r\n    bool HasException() const { return state_ == State::Exception; }\r\n    \r\n    void Check() const\r\n    {\r\n        if (state_ == State::Exception)\r\n            std::rethrow_exception(exception_);\r\n    }\r\n\r\n    // Amazing! Thanks to folly\r\n    template <typename R>\r\n    R Get() { return std::forward<R>(*this); }\r\n\r\nprivate:\r\n    State state_;\r\n    std::exception_ptr exception_;\r\n};\r\n\r\n\r\n// Wrap function f(...) return by Try<T>\r\ntemplate <typename F, typename... Args>\r\ntypename std::enable_if<\r\n    !std::is_same<typename std::result_of<F (Args...)>::type, void>::value,\r\n    Try<typename std::result_of<F (Args...)>::type >> ::type\r\n    WrapWithTry(F&& f, Args&&... args)\r\n{\r\n    using Type = typename std::result_of<F(Args...)>::type;\r\n\r\n    try\r\n    {\r\n        return Try<Type>(std::forward<F>(f)(std::forward<Args>(args)...));\r\n    }\r\n    catch (std::exception& e) \r\n    {\r\n        return Try<Type>(std::current_exception());\r\n    }\r\n}\r\n\r\n// Wrap void function f(...) return by Try<void>\r\ntemplate <typename F, typename... Args>\r\ntypename std::enable_if <\r\n    std::is_same<typename std::result_of<F (Args...)>::type, void>::value,\r\n    Try<void>> ::type\r\n    WrapWithTry(F&& f, Args&&... args)\r\n{\r\n    try\r\n    {\r\n        std::forward<F>(f)(std::forward<Args>(args)...);\r\n        return Try<void>();\r\n    }\r\n    catch (std::exception& e)\r\n    {\r\n        return Try<void>(std::current_exception());\r\n    }\r\n}\r\n\r\n// f's arg is void, but return Type\r\n// Wrap return value of function Type f(void) by Try<Type>\r\ntemplate <typename F>\r\ntypename std::enable_if<\r\n    !std::is_same<typename std::result_of<F ()>::type, void>::value,\r\n    Try<typename std::result_of<F ()>::type >> ::type\r\n    WrapWithTry(F&& f, Try<void>&& arg)\r\n{\r\n    using Type = typename std::result_of<F()>::type;\r\n\r\n    try\r\n    {\r\n        return Try<Type>(std::forward<F>(f)());\r\n    }\r\n    catch (std::exception& e) \r\n    {\r\n        return Try<Type>(std::current_exception());\r\n    }\r\n}\r\n\r\n// Wrap return value of function void f(void) by Try<void>\r\ntemplate <typename F>\r\ntypename std::enable_if <\r\n    std::is_same<typename std::result_of<F ()>::type, void>::value,\r\n    Try<typename std::result_of<F ()>::type >> ::type\r\n    WrapWithTry(F&& f, Try<void>&& arg)\r\n{\r\n    try\r\n    {\r\n        std::forward<F>(f)();\r\n        return Try<void>();\r\n    }\r\n    catch (std::exception& e) \r\n    {\r\n        return Try<void>(std::current_exception());\r\n    }\r\n}\r\n\r\n} // end namespace ananas\r\n\r\n#endif\r\n\r\n"
  },
  {
    "path": "cluster/ananas/net/Acceptor.cc",
    "content": "#include <errno.h>\n#include <cassert>\n#include \"EventLoop.h\"\n#include \"Connection.h\"\n#include \"Acceptor.h\"\n\n#include \"AnanasDebug.h\"\n\n\nnamespace ananas\n{\nnamespace internal\n{\n\nconst int Acceptor::kListenQueue = 1024;\n\nAcceptor::Acceptor(EventLoop* loop) :\n    localSock_(kInvalid),\n    localPort_(SocketAddr::kInvalidPort),\n    loop_(loop)\n{\n}\n\nAcceptor::~Acceptor()\n{\n    CloseSocket(localSock_);\n    INF(internal::g_debug) << \"Close Acceptor \" << localPort_ ;\n}\n\n\nvoid Acceptor::SetNewConnCallback(NewTcpConnCallback cb)\n{\n    newConnCallback_ = std::move(cb);\n}\n\nbool Acceptor::Bind(const SocketAddr& addr)\n{\n    if (!addr.IsValid())\n        return false;\n\n    if (localSock_ != kInvalid)\n    {\n        ERR(internal::g_debug) << \"Already listen \" << localPort_;\n        return false;\n    }\n\n    localSock_ = CreateTCPSocket();\n    if (localSock_ == kInvalid)\n        return false;\n\n    localPort_ = addr.GetPort();\n\n    SetNonBlock(localSock_);\n    SetNodelay(localSock_);\n    SetReuseAddr(localSock_);\n    SetRcvBuf(localSock_);\n    SetSndBuf(localSock_);\n\n    auto serv = addr.GetAddr();\n\n    int ret = ::bind(localSock_, (struct sockaddr*)&serv, sizeof serv);\n    if (kError == ret)\n    {\n        ERR(internal::g_debug) << \"Cannot bind to \" << addr.ToString();\n        return false;\n    }\n\n    ret = ::listen(localSock_, kListenQueue);\n    if (kError == ret)\n    {\n        ERR(internal::g_debug) << \"Cannot listen on \" << addr.ToString();\n        return false;\n    }\n\n    if (!loop_->Register(eET_Read, this))\n        return false;\n\n    INF(internal::g_debug) << \"Create listen socket \" << localSock_\n                           << \" on port \" << localPort_;\n    return  true;\n}\n\nint Acceptor::Identifier() const\n{\n    return localSock_;\n}\n\nbool Acceptor::HandleReadEvent() \n{\n    while (true)\n    {\n        int connfd = _Accept();\n        if (connfd != kInvalid)\n        {\n            auto conn(std::make_shared<Connection>(loop_));\n            conn->Init(connfd, peer_);\n\n            if (loop_->Register(eET_Read, conn.get()))\n            {\n                newConnCallback_(conn.get());\n                conn->_OnConnect();\n            }\n            else\n            {\n                ERR(internal::g_debug) << \"Failed to register socket \" << conn->Identifier();\n            }\n        }\n        else\n        {\n            bool goAhead = false;\n            const int error = errno;\n            switch (error)\n            {\n            //case EWOULDBLOCK:\n            case EAGAIN:\n                return true; // it's fine\n\n            case EINTR:\n            case ECONNABORTED:\n            case EPROTO:\n                goAhead = true; // should retry\n                break;\n\n            case EMFILE:\n            case ENFILE:\n                ERR(internal::g_debug) << \"Not enough file descriptor available, error is \"\n                                       << error\n                                       << \", CPU may 100%\";\n                return true;\n\n            case ENOBUFS:\n            case ENOMEM:\n                ERR(internal::g_debug) << \"Not enough memory, limited by the socket buffer limits\"\n                                       << \", CPU may 100%\";\n                return true;\n\n            case ENOTSOCK: \n            case EOPNOTSUPP:\n            case EINVAL:\n            case EFAULT:\n            case EBADF:\n            default:\n                ERR(internal::g_debug) << \"BUG: error = \" << error;\n                assert (false);\n                break;\n            }\n\n            if (!goAhead)\n                return false;\n        }\n    }\n    \n    return true;\n}\n\nbool Acceptor::HandleWriteEvent()\n{\n    assert (false);\n    return false;\n}\n\nvoid Acceptor::HandleErrorEvent()\n{\n    ERR(internal::g_debug) << \"Acceptor::HandleErrorEvent\";\n    loop_->Unregister(eET_Read, this);\n}\n\nint Acceptor::_Accept()\n{\n    socklen_t addrLength = sizeof peer_;\n    return ::accept(localSock_, (struct sockaddr *)&peer_, &addrLength);\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Acceptor.h",
    "content": "\n#ifndef BERT_ACCEPTOR_H\n#define BERT_ACCEPTOR_H\n\n#include \"Socket.h\"\n#include \"Typedefs.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nclass Acceptor : public EventSource\n{\npublic:\n    explicit\n    Acceptor(EventLoop* loop);\n    ~Acceptor();\n    \n    Acceptor(const Acceptor& ) = delete;\n    void operator= (const Acceptor& ) = delete;\n\n    void SetNewConnCallback(NewTcpConnCallback cb);\n    bool Bind(const SocketAddr& addr);\n        \n    int Identifier() const override;\n    bool HandleReadEvent() override;\n    bool HandleWriteEvent() override;\n    void HandleErrorEvent() override;\n\nprivate:\n    int _Accept();\n\n    SocketAddr peer_;\n    int localSock_;\n    uint16_t localPort_;\n\n    EventLoop* const loop_; // which loop belong to\n\n    //register msg callback and on connect callback for conn\n    NewTcpConnCallback newConnCallback_;\n\n    static const int kListenQueue;\n};\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/AnanasDebug.cc",
    "content": "#include \"AnanasDebug.h\"\n\nnamespace ananas\n{\n\nnamespace internal\n{\n\nstd::shared_ptr<Logger> g_debug;\nstd::once_flag g_logInit;\n\nvoid InitDebugLog(unsigned int level)\n{\n    std::call_once(g_logInit, [level]() {\n            g_debug = LogManager::Instance().CreateLog(level, logFile, \"ananas_debug_log\");\n        });\n}\n\n} // end namespace internal\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/AnanasDebug.h",
    "content": "#ifndef BERT_ANANASDEBUG_H\n#define BERT_ANANASDEBUG_H\n\n#include <mutex>\n#include \"log/Logger.h\"\n\nnamespace ananas\n{\n\nnamespace internal\n{\n    \nextern std::shared_ptr<Logger> g_debug;\nextern std::once_flag g_logInit;\nvoid InitDebugLog(unsigned int level);\n\n} // end namespace internal\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/AnanasLogo.h",
    "content": "#ifndef BERT_ANANASLOGO_H\n#define BERT_ANANASLOGO_H\n\nnamespace ananas\n{\nnamespace internal\n{\n\nconst char* logo = \"\"\n\"  __ _ _ __   __ _ _ __   __ _ ___  \\n\"\n\" / _` | '_ \\\\ / _` | '_ \\\\ / _` / __| \\n\"\n\"| (_| | | | | (_| | | | | (_| \\\\__ \\\\ \\n\"\n\" \\\\__,_|_| |_|\\\\__,_|_| |_|\\\\__,_|___/ \\n\";\n\n}\n}\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Buffer.cc",
    "content": "\n#include \"Buffer.h\"\n#include <iostream>\n#include <limits>\n#include <cassert>\n\nnamespace ananas\n{\n\ninline static std::size_t RoundUp2Power(std::size_t size)\n{\n    if (size == 0)\n        return 0;\n\n    std::size_t roundUp = 1;\n    while (roundUp < size)\n        roundUp *= 2;\n\n    return roundUp;\n}\n\n\nconst std::size_t Buffer::kMaxBufferSize = std::numeric_limits<std::size_t>::max() / 2;\nconst std::size_t Buffer::kHighWaterMark = 1 * 1024;\nconst std::size_t Buffer::kDefaultSize = 64;\n\nstd::size_t Buffer::PushData(const void* data, std::size_t size)\n{\n    std::size_t bytes = PushDataAt(data, size);\n    Produce(bytes);\n\n    assert (bytes == size);\n\n    return bytes;\n}\n\nstd::size_t Buffer::PushDataAt(const void* data, std::size_t size, std::size_t offset)\n{\n    if (!data || size == 0)\n        return 0;\n\n    if (ReadableSize() + size + offset >= kMaxBufferSize)\n        return 0; // overflow\n\n    AssureSpace(size + offset);\n\n    assert (size + offset <= WritableSize());\n\n    ::memcpy(&buffer_[writePos_ + offset], data, size);\n    return  size;\n}\n\nstd::size_t Buffer::PopData(void* buf, std::size_t size)\n{\n    std::size_t bytes = PeekDataAt(buf, size);\n    Consume(bytes);\n\n    return bytes;\n}\n    \nvoid Buffer::Consume(std::size_t bytes)\n{\n    assert (readPos_ + bytes <= writePos_);\n\n    readPos_ += bytes;\n    if (IsEmpty())\n        Clear();\n}\n\nstd::size_t Buffer::PeekDataAt(void* buf, std::size_t size, std::size_t offset)\n{\n    const std::size_t dataSize = ReadableSize();\n    if (!buf ||\n         size == 0 ||\n         dataSize <= offset)\n        return 0;\n\n    if (size + offset > dataSize)\n        size = dataSize - offset; // truncate\n\n    ::memcpy(buf, &buffer_[readPos_ + offset], size);\n    return size;\n}\n\n\nvoid Buffer::AssureSpace(std::size_t needsize)\n{\n    if (WritableSize() >= needsize)\n        return;\n\n    const size_t dataSize = ReadableSize();\n    const size_t oldCap = capacity_;\n\n    while (WritableSize() + readPos_ < needsize)\n    {\n        if (capacity_ < kDefaultSize)\n        {\n            capacity_ = kDefaultSize;\n        }\n        else if (capacity_ <= kMaxBufferSize)\n        {\n            const auto newCapcity = RoundUp2Power(capacity_);\n            if (capacity_ < newCapcity)\n                capacity_ = newCapcity;\n            else\n                capacity_ = 2 * newCapcity;\n        }\n        else \n        {\n            assert (false);\n        }\n    }\n\n    if (oldCap < capacity_)\n    {\n        std::unique_ptr<char []> tmp(new char[capacity_]);\n\n        if (dataSize != 0)\n            memcpy(&tmp[0], &buffer_[readPos_], dataSize);\n\n        buffer_.swap(tmp);\n        //std::cout << \" expand to \" << capacity_ << \", and data size \" << dataSize << std::endl;\n    }\n    else\n    {\n        assert (readPos_ > 0);\n\n        ::memmove(&buffer_[0], &buffer_[readPos_], dataSize);\n        std::cout << \" move from \" << readPos_ << \", and dataSize \" << dataSize << std::endl;\n    }\n\n    readPos_ = 0;\n    writePos_ = dataSize;\n}\n\n\nvoid Buffer::Shrink()\n{\n    if (IsEmpty())\n    {\n        Clear();\n        capacity_ = 0;\n        buffer_.reset();\n        return;\n    }\n\n    std::size_t oldCap = capacity_;\n    std::size_t dataSize = ReadableSize();\n    if (dataSize > oldCap / 2)\n        return;\n\n    std::size_t newCap = RoundUp2Power(dataSize);\n\n    std::unique_ptr<char []> tmp(new char[newCap]);\n    memcpy(&tmp[0], &buffer_[readPos_], dataSize);\n    buffer_.swap(tmp);\n    capacity_ = newCap;\n\n    readPos_  = 0;\n    writePos_ = dataSize;\n\n    std::cout << oldCap << \" shrink to \" << capacity_ << std::endl;\n}\n\nvoid Buffer::Clear()\n{\n    readPos_ = writePos_ = 0; \n}\n\n\nvoid Buffer::Swap(Buffer& buf)\n{\n    std::swap(readPos_, buf.readPos_);\n    std::swap(writePos_, buf.writePos_);\n    std::swap(capacity_, buf.capacity_);\n    buffer_.swap(buf.buffer_);\n}\n    \nBuffer::Buffer(Buffer&& other)\n{\n    _MoveFrom(std::move(other));\n}\n\nBuffer& Buffer::operator= (Buffer&& other) \n{\n    return _MoveFrom(std::move(other));\n}\n\nBuffer& Buffer::_MoveFrom(Buffer&& other)\n{\n    if (this != &other)\n    {\n        this->readPos_ = other.readPos_;\n        this->writePos_ = other.writePos_;\n        this->capacity_ = other.capacity_;\n        this->buffer_ = std::move(other.buffer_);\n\n        other.Clear();\n        other.capacity_ = 0;\n    }\n\n    return *this;\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Buffer.h",
    "content": "\n#ifndef BERT_BUFFER_H\n#define BERT_BUFFER_H\n\n#include <cstring>\n#include <memory>\n#include <list>\n\nnamespace ananas\n{\n\nclass Buffer\n{\npublic:\n    Buffer() :\n        readPos_(0),\n        writePos_(0),\n        capacity_(0)\n    {\n    }\n\n    Buffer(const void* data, size_t size) :\n        readPos_(0),\n        writePos_(0),\n        capacity_(0)\n    {\n        PushData(data, size);\n    }\n\n    Buffer(const Buffer& ) = delete;\n    void operator = (const Buffer& ) = delete;\n\n    Buffer(Buffer&& ) ;\n    Buffer& operator = (Buffer&& ) ;\n\n    std::size_t PushDataAt(const void* data, std::size_t size, std::size_t offset = 0);\n    std::size_t PushData(const void* data, std::size_t size);\n    void Produce(std::size_t bytes) {   writePos_ += bytes; }\n\n    std::size_t PeekDataAt(void* pBuf, std::size_t size, std::size_t offset = 0);\n    std::size_t PopData(void* pBuf, std::size_t size);\n    void Consume(std::size_t bytes);\n\n    char* ReadAddr()  {  return &buffer_[readPos_];  }\n    char* WriteAddr() {  return &buffer_[writePos_]; }\n\n    bool IsEmpty() const { return ReadableSize() == 0; }\n    std::size_t ReadableSize() const {  return writePos_ - readPos_;  }\n    std::size_t WritableSize() const {  return capacity_ - writePos_;  }\n    std::size_t Capacity() const {  return capacity_;  }\n\n    void Shrink();\n    void Clear();\n    void Swap(Buffer& buf);\n\n    void AssureSpace(std::size_t size);\n\n    static const std::size_t  kMaxBufferSize;\n    static const std::size_t  kHighWaterMark;\n    static const std::size_t  kDefaultSize;\n\nprivate:\n    Buffer& _MoveFrom(Buffer&& );\n\n    std::size_t readPos_;\n    std::size_t writePos_;\n    std::size_t capacity_;\n    std::unique_ptr<char []>  buffer_;\n};\n\n\nstruct BufferVector\n{\n    typedef std::list<Buffer> BufferContainer;\n\n    typedef BufferContainer::const_iterator const_iterator;\n    typedef BufferContainer::iterator iterator;\n    typedef BufferContainer::value_type value_type;\n    typedef BufferContainer::reference reference;\n    typedef BufferContainer::const_reference const_reference;\n\n    BufferVector()\n    {\n    }\n\n    BufferVector(Buffer&& first)\n    {\n        PushBack(std::move(first));\n    }\n\n    bool Empty() const { return buffers.empty(); }\n\n    std::size_t TotalBytes() const\n    {\n        size_t s = 0;\n        for (const auto& buf : buffers)\n            s += buf.ReadableSize();\n\n        return s;\n    }\n\n    void Clear()\n    {\n        buffers.clear();\n    }\n\n    void PushBack(Buffer&& buf)\n    {\n        buffers.push_back(std::move(buf));\n    }\n\n    void PushFront(Buffer&& buf)\n    {\n        buffers.push_front(std::move(buf));\n    }\n\n    void PopBack()\n    {\n        buffers.pop_back();\n    }\n\n    void PopFront()\n    {\n        buffers.pop_front();\n    }\n\n    iterator begin() { return buffers.begin(); }\n    iterator end() { return buffers.end(); }\n\n    const_iterator begin() const { return buffers.begin(); }\n    const_iterator end() const { return buffers.end(); }\n\n    const_iterator cbegin() const { return buffers.cbegin(); }\n    const_iterator cend() const { return buffers.cend(); }\n\nprivate:\n    BufferContainer buffers;\n};\n\nstruct Slice\n{\n    const void* data;\n    size_t len;\n\n    explicit\n    Slice(const void* d = nullptr, size_t l = 0) :\n        data(d),\n        len(l)\n    {}\n};\n\nstruct SliceVector\n{\nprivate:\n    typedef std::list<Slice> Slices;\n\npublic:\n\n    typedef Slices::const_iterator const_iterator;\n    typedef Slices::iterator iterator;\n    typedef Slices::value_type value_type;\n    typedef Slices::reference reference;\n    typedef Slices::const_reference const_reference;\n\n    iterator begin() { return slices.begin(); }\n    iterator end() { return slices.end(); }\n\n    const_iterator begin() const { return slices.begin(); }\n    const_iterator end() const { return slices.end(); }\n\n    const_iterator cbegin() const { return slices.cbegin(); }\n    const_iterator cend() const { return slices.cend(); }\n\n    bool Empty() const { return slices.empty(); }\n\n    void PushBack(const void* data, size_t len)\n    {\n        slices.push_back(Slice(data, len));\n    }\n\nprivate:\n    Slices slices;\n};\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.8)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ananas)\n\nAUX_SOURCE_DIRECTORY(. ANANAS_SRC)\nAUX_SOURCE_DIRECTORY(./log ANANAS_SRC)\nSET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)\n\nIF(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n    REMOVE(${ANANAS_SRC} Kqueue.cc Kqueue.h)\nELSEIF(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n    REMOVE(${ANANAS_SRC} Epoller.cc Epoller.h)\nENDIF()\n\n\nADD_LIBRARY(ananas ${ANANAS_SRC})\nTARGET_LINK_LIBRARIES(ananas; pthread)\nSET_TARGET_PROPERTIES(ananas PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "cluster/ananas/net/Connection.cc",
    "content": "#include <cassert>\n\n#include <errno.h>\n#include <unistd.h>\n#include <sys/uio.h>\n\n#include \"EventLoop.h\"\n#include \"Connection.h\"\n#include \"AnanasDebug.h\"\n#include \"util/Util.h\"\n\nnamespace ananas\n{\n\nConnection::Connection(EventLoop* loop) :\n    loop_(loop),\n    localSock_(kInvalid),\n    minPacketSize_(1),\n    sendBufHighWater_(10 * 1024 * 1024)\n{\n}\n\nConnection::~Connection()\n{\n    if (localSock_ != kInvalid)\n    {\n        Shutdown(ShutdownMode::eSM_Both); // Force send FIN\n        CloseSocket(localSock_);\n    }\n}\n\nbool Connection::Init(int fd, const SocketAddr& peer)\n{\n    if (fd == kInvalid)\n        return false;\n\n    localSock_ = fd;\n    SetNonBlock(localSock_);\n\n    peer_ = peer;\n\n    assert (state_ == State::eS_None);\n    state_ = State::eS_Connected;\n    return true;\n}\n\nvoid Connection::ActiveClose()\n{\n    if (localSock_ == kInvalid)\n        return;\n\n    state_ = State::eS_ActiveClose;\n    loop_->Modify(internal::eET_Write, this);\n}\n\nvoid Connection::Shutdown(ShutdownMode mode)\n{\n    switch (mode)\n    {\n    case ShutdownMode::eSM_Read:\n        ::shutdown(localSock_, SHUT_RD);\n        break;\n\n    case ShutdownMode::eSM_Write:\n        if (!sendBuf_.Empty())\n        {\n            WRN(internal::g_debug) << localSock_\n                                   << \" shutdown write, but still has data to send\";\n            sendBuf_.Clear();\n        }\n\n        ::shutdown(localSock_, SHUT_WR);\n        break;\n\n    case ShutdownMode::eSM_Both:\n        if (!sendBuf_.Empty())\n        {\n            WRN(internal::g_debug) << localSock_\n                                   << \" shutdown both, but still has data to send\";\n            sendBuf_.Clear();\n        }\n\n        ::shutdown(localSock_, SHUT_RDWR);\n        break;\n    }\n}\n\nvoid Connection::SetNodelay(bool enable)\n{\n    ananas::SetNodelay(localSock_, enable);\n}\n\nint Connection::Identifier() const\n{\n    return localSock_;\n}\n\nbool Connection::HandleReadEvent()\n{\n    if (state_ != State::eS_Connected)\n    {\n        ERR(internal::g_debug) << localSock_\n                               << \" HandleReadEvent error \" << state_;\n        return false;\n    }\n\n    bool busy = false;\n    while (true)\n    {\n        recvBuf_.AssureSpace(4 * 1024);\n        char stack[128 * 1024];\n\n        struct iovec vecs[2];\n        vecs[0].iov_base = recvBuf_.WriteAddr();\n        vecs[0].iov_len = recvBuf_.WritableSize();\n        vecs[1].iov_base = stack;\n        vecs[1].iov_len = sizeof stack;\n\n        int bytes = ::readv(localSock_, vecs, 2);\n        if (kError == bytes)\n        {\n            if (EAGAIN == errno || EWOULDBLOCK == errno)\n                return true;\n\n            if (EINTR == errno)\n                continue; // restart ::readv\n        }\n\n        if (0 == bytes)\n        {\n            WRN(internal::g_debug) << localSock_ << \" HandleReadEvent EOF \";\n            if (sendBuf_.Empty())\n            {\n                state_ = State::eS_PassiveClose;\n            }\n            else\n            {\n                state_ = State::eS_CloseWaitWrite;\n                loop_->Modify(internal::eET_Write, this); // disable ReadEvent\n            }\n\n            return false;\n        }\n\n        if (bytes < 0)\n        {\n            ERR(internal::g_debug) << localSock_ << \" HandleReadEvent Error\";\n            state_ = State::eS_Error;\n            return false;\n        }\n       \n        if (static_cast<size_t>(bytes) <= vecs[0].iov_len) \n        {\n            recvBuf_.Produce(static_cast<size_t>(bytes));\n        }\n        else\n        {\n            recvBuf_.Produce(vecs[0].iov_len);\n\n            auto stackBytes = static_cast<size_t>(bytes) - vecs[0].iov_len;\n            recvBuf_.PushData(stack, stackBytes);\n        }\n\n        while (recvBuf_.ReadableSize() >= minPacketSize_)\n        {\n            assert (!recvBuf_.IsEmpty());\n            auto bytes = onMessage_(this, recvBuf_.ReadAddr(), recvBuf_.ReadableSize());\n\n            if (bytes == 0)\n            {\n                break;\n            }\n            else\n            {\n                recvBuf_.Consume(bytes);\n                busy = true;\n            }\n        }\n    }\n\n    if (busy)\n        recvBuf_.Shrink();\n\n    return true;\n}\n\n\nint Connection::_Send(const void* data, size_t len)\n{\n    if (len == 0)\n        return 0;\n\n    int  bytes = ::send(localSock_, data, len, 0);\n    if (kError == bytes)\n    {\n        if (EAGAIN == errno || EWOULDBLOCK == errno)\n            bytes = 0;\n\n        if (EINTR == errno)\n            bytes = 0; // later try ::send\n    }\n\n    if (kError == bytes)\n    {\n        ERR(internal::g_debug) << localSock_ << \" _Send Error\";\n        state_ = State::eS_Error;\n    }\n\n    return bytes;\n}\n\nnamespace\n{\nint WriteV(int sock, const std::vector<iovec>& buffers);\nvoid ConsumeBufferVectors(BufferVector& buffers, size_t toSkippedBytes);\nvoid CollectBuffer(const std::vector<iovec>& buffers, size_t skipped, BufferVector& dst);\n}\n\nbool Connection::HandleWriteEvent()\n{\n    if (state_ != State::eS_Connected &&\n        state_ != State::eS_CloseWaitWrite)\n    {\n        ERR(internal::g_debug) << localSock_ << \" HandleWriteEvent wrong state \" << state_;\n        return false;\n    }\n\n    // it's connected or half-close, whatever, we can send.\n\n    size_t expectSend = 0;\n    std::vector<iovec> iovecs;\n    for (auto& e : sendBuf_)\n    {\n        assert (!e.IsEmpty());\n\n        iovec ivc; \n        ivc.iov_base = (void*)(e.ReadAddr());\n        ivc.iov_len = e.ReadableSize();\n\n        iovecs.push_back(ivc);\n        expectSend += e.ReadableSize();\n    }\n\n    int ret = WriteV(localSock_, iovecs);\n    if (ret == kError)\n    {\n        ERR(internal::g_debug) << localSock_ << \" HandleWriteEvent ERROR \";\n        state_ = State::eS_Error;\n        return false;\n    }\n\n    assert (ret >= 0);\n\n    size_t alreadySent = static_cast<size_t>(ret);\n    ConsumeBufferVectors(sendBuf_, alreadySent);\n\n    if (alreadySent == expectSend)\n    {\n        DBG(internal::g_debug) << localSock_ << \" HandleWriteEvent complete\";\n        loop_->Modify(internal::eET_Read, this);\n\n        if (onWriteComplete_)\n            onWriteComplete_(this);\n\n        if (state_ == State::eS_CloseWaitWrite)\n        {\n            state_ = State::eS_PassiveClose;\n            return false;\n        }\n    }\n\n    return true;\n}\n\nvoid  Connection::HandleErrorEvent()\n{\n    ERR(internal::g_debug) << localSock_ << \" HandleErrorEvent \" << state_;\n\n    switch (state_)\n    {\n    case State::eS_PassiveClose:\n    case State::eS_ActiveClose:\n    case State::eS_Error:\n        break;\n\n    case State::eS_None:\n    case State::eS_Connected: // should not happen\n    case State::eS_CloseWaitWrite:\n    case State::eS_Closed: // should not happen\n    default:\n        return;\n    }\n\n    state_ = State::eS_Closed;\n\n    if (onDisconnect_) \n        onDisconnect_(this);\n\n    if (onConnFail_)\n        onConnFail_(loop_, peer_);\n\n    loop_->Unregister(internal::eET_Read | internal::eET_Write, this);\n}\n\nbool Connection::SendPacket(const void* data, std::size_t size)\n{\n    if (size == 0)\n        return true;\n\n    if (state_ != State::eS_Connected &&\n        state_ != State::eS_CloseWaitWrite)\n        return false;\n\n    const size_t oldSendBytes = sendBuf_.TotalBytes();\n    ANANAS_DEFER\n    {\n        size_t nowSendBytes = sendBuf_.TotalBytes();\n        if (oldSendBytes < sendBufHighWater_ &&\n            nowSendBytes >= sendBufHighWater_)\n        {\n            if (onWriteHighWater)\n                onWriteHighWater(this, nowSendBytes);\n        }\n    };\n\n    if (oldSendBytes > 0)\n    {\n        sendBuf_.PushBack(Buffer(data, size));\n        return true;\n    }\n        \n    auto bytes = _Send(data, size);\n    if (bytes == kError)\n    {\n        state_ = State::eS_Error;\n        loop_->Modify(internal::eET_Write, this);\n        return false;\n    }\n\n    if (bytes < static_cast<int>(size))\n    {\n        WRN(internal::g_debug) << localSock_\n                               << \" want send \"\n                               << size\n                               << \" bytes, but only send \"\n                               << bytes;\n        sendBuf_.PushBack(Buffer((char*)data + bytes, size - static_cast<std::size_t>(bytes)));\n        loop_->Modify(internal::eET_Read | internal::eET_Write, this);\n    }\n    else\n    {\n        if (onWriteComplete_)\n            onWriteComplete_(this);\n    }\n\n    return true;\n}\n\n// iovec for writev\nnamespace\n{\n\nint WriteV(int sock, const std::vector<iovec>& buffers)\n{\n    const int kIOVecCount = 64; // be care of IOV_MAX\n\n    size_t sentVecs = 0;\n    size_t sentBytes = 0;\n    while (sentVecs < buffers.size())\n    {\n        const int vc = std::min<int>(buffers.size() - sentVecs, kIOVecCount);\n\n        int expectBytes = 0;\n        for (size_t i = sentVecs; i < sentVecs + vc; ++ i)\n        {\n            expectBytes += static_cast<int>(buffers[i].iov_len);\n        }\n\n        assert (expectBytes > 0);\n        int bytes = static_cast<int>(::writev(sock, &buffers[sentVecs], vc));\n        assert (bytes != 0);\n\n        if (kError == bytes)\n        {\n            assert (errno != EINVAL);\n\n            if (EAGAIN == errno || EWOULDBLOCK == errno)\n                return static_cast<int>(sentBytes);\n\n            if (EINTR == errno)\n                continue; // retry\n\n            return kError;  // can not send any more\n        }\n        else\n        {\n            assert (bytes > 0);\n            sentBytes += bytes;\n            if (bytes == expectBytes)\n                sentVecs += vc;\n            else\n                return sentBytes;\n        }\n    }\n\n    return sentBytes;\n}\n\nvoid ConsumeBufferVectors(BufferVector& buffers, size_t toSkippedBytes)\n{\n    size_t skippedVecs = 0;\n    for (auto& e : buffers)\n    {\n        assert (e.ReadableSize() > 0);\n\n        if (toSkippedBytes >= e.ReadableSize())\n        {\n            toSkippedBytes -= e.ReadableSize();\n            ++ skippedVecs;\n        }\n        else\n        {\n            if (toSkippedBytes > 0)\n            {\n                e.Consume(toSkippedBytes);\n            }\n\n            break;\n        }\n    }\n\n    while (skippedVecs-- > 0)\n        buffers.PopFront();\n}\n\nvoid CollectBuffer(const std::vector<iovec>& buffers, size_t skipped, BufferVector& dst)\n{\n    for (auto e : buffers)\n    {\n        if (skipped >= e.iov_len)\n        {\n            skipped -= e.iov_len;\n        }\n        else\n        {\n            dst.PushBack(Buffer((char*)e.iov_base + skipped, e.iov_len - skipped));\n                \n            if (skipped != 0)\n                skipped = 0;\n        }\n    }\n}\n\n} // end namespace\n\nbool Connection::SendPacket(const BufferVector& data)\n{\n    if (state_ != State::eS_Connected &&\n        state_ != State::eS_CloseWaitWrite)\n        return false;\n\n    SliceVector s;\n    for (const auto& d : data)\n    {\n        s.PushBack(const_cast<Buffer&>(d).ReadAddr(), d.ReadableSize());\n    }\n\n    return SendPacket(s);\n}\n\nbool Connection::SendPacket(const SliceVector& slices)\n{\n    if (slices.Empty())\n        return true;\n\n    const size_t oldSendBytes = sendBuf_.TotalBytes();\n    ANANAS_DEFER\n    {\n        size_t nowSendBytes = sendBuf_.TotalBytes();\n        if (oldSendBytes < sendBufHighWater_ &&\n            nowSendBytes >= sendBufHighWater_)\n        {\n            if (onWriteHighWater)\n                onWriteHighWater(this, nowSendBytes);\n        }\n    };\n\n    if (oldSendBytes > 0)\n    {\n        for (const auto& e : slices)\n        {\n            sendBuf_.PushBack(Buffer(e.data, e.len));\n        }\n\n        return true;\n    }\n\n    size_t expectSend = 0;\n    std::vector<iovec> iovecs;\n    for (const auto& e : slices)\n    {\n        if (e.len == 0)\n            continue;\n\n        iovec ivc; \n        ivc.iov_base = const_cast<void*>(e.data);\n        ivc.iov_len = e.len;\n\n        iovecs.push_back(ivc);\n        expectSend += e.len;\n    }\n\n    int ret = WriteV(localSock_, iovecs);\n    if (ret == kError)\n    {\n        state_ = State::eS_Error;\n        loop_->Modify(internal::eET_Write, this);\n        return false;\n    }\n\n    assert (ret >= 0);\n\n    size_t alreadySent = static_cast<size_t>(ret);\n    if (alreadySent < expectSend)\n    {\n        CollectBuffer(iovecs, alreadySent, sendBuf_);\n        loop_->Modify(internal::eET_Read | internal::eET_Write, this);\n    }\n    else\n    {\n        if (onWriteComplete_)\n            onWriteComplete_(this);\n    }\n\n    return true;\n}\n\nvoid Connection::SetOnConnect(std::function<void (Connection* )> cb)\n{\n    onConnect_ = std::move(cb);\n}\n\nvoid Connection::SetOnDisconnect(std::function<void (Connection* )> cb)\n{\n    onDisconnect_ = std::move(cb);\n}\n\nvoid Connection::SetOnMessage(TcpMessageCallback cb)\n{\n    onMessage_ = std::move(cb);\n}\n\nvoid Connection::_OnConnect()\n{\n    if (state_ != State::eS_Connected)\n        return;\n\n    if (onConnect_)\n        onConnect_(this);\n}\n\nvoid Connection::SetFailCallback(TcpConnFailCallback cb)\n{\n    onConnFail_ = std::move(cb);\n}\n\nvoid Connection::SetOnWriteComplete(TcpWriteCompleteCallback wccb)\n{\n    onWriteComplete_ = std::move(wccb);\n}\n\nvoid Connection::SetMinPacketSize(size_t s)\n{\n    minPacketSize_ = s;\n}\n\nvoid Connection::SetWriteHighWater(size_t s)\n{\n    sendBufHighWater_ = s;\n}\n\nvoid Connection::SetOnWriteHighWater(TcpWriteHighWaterCallback whwcb)\n{\n    onWriteHighWater = std::move(whwcb);\n}\n\nvoid Connection::SetUserData(std::shared_ptr<void> user)\n{\n    userData_ = std::move(user);\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Connection.h",
    "content": "\n#ifndef BERT_CONNECTION_H\n#define BERT_CONNECTION_H\n\n#include <sys/types.h>\n#include \"Socket.h\"\n#include \"Poller.h\"\n#include \"Buffer.h\"\n#include \"Typedefs.h\"\n\nnamespace ananas\n{\n\nnamespace internal\n{\nclass Acceptor;\nclass Connector;\n}\n\nenum class ShutdownMode\n{\n    eSM_Both,\n    eSM_Read,\n    eSM_Write,\n};\n\nclass Connection : public internal::EventSource\n{\npublic:\n    explicit\n    Connection(EventLoop* loop);\n    ~Connection();\n\n    Connection(const Connection& ) = delete;\n    void operator= (const Connection& ) = delete;\n\n    bool Init(int sock, const SocketAddr& peer);\n    void SetMaxPacketSize(std::size_t s);\n    const SocketAddr& Peer() const { return peer_; }\n    void ActiveClose();\n    EventLoop* GetLoop() const { return loop_; }\n\n    void Shutdown(ShutdownMode mode);\n    void SetNodelay(bool enable);\n\n    int Identifier() const override;\n    bool HandleReadEvent() override;\n    bool HandleWriteEvent() override;\n    void HandleErrorEvent() override;\n\n    bool SendPacket(const void* data, std::size_t len);\n    bool SendPacket(const BufferVector& datum);\n    bool SendPacket(const SliceVector& slice);\n\n    void SetOnConnect(std::function<void (Connection* )> cb);\n    void SetOnDisconnect(std::function<void (Connection* )> cb);\n    void SetOnMessage(TcpMessageCallback cb);\n    void SetFailCallback(TcpConnFailCallback cb);\n    void SetOnWriteComplete(TcpWriteCompleteCallback wccb);\n    void SetOnWriteHighWater(TcpWriteHighWaterCallback whwcb);\n\n    void SetMinPacketSize(size_t s);\n    void SetWriteHighWater(size_t s);\n\n    void SetUserData(std::shared_ptr<void> user);\n\n    template <typename T>\n    std::shared_ptr<T> GetUserData() const;\n\nprivate:\n    enum State\n    {\n        eS_None,\n        eS_Connected,\n        eS_CloseWaitWrite, // peer close or shutdown write, but I have data to send\n        eS_PassiveClose, // should close\n        eS_ActiveClose, // should close\n        eS_Error,\n        eS_Closed,\n    };\n\n    friend class internal::Acceptor;\n    friend class internal::Connector;\n    void _OnConnect();\n    int _Send(const void* data, size_t len);\n\n    EventLoop* const loop_;\n    State state_ = State::eS_None;\n    int localSock_;\n    size_t minPacketSize_;\n    size_t sendBufHighWater_;\n\n    Buffer recvBuf_;\n    BufferVector sendBuf_;\n\n    SocketAddr peer_;\n\n    std::function<void (Connection* )> onConnect_;\n    std::function<void (Connection* )> onDisconnect_;\n    \n    TcpMessageCallback onMessage_;\n    TcpConnFailCallback onConnFail_;\n    TcpWriteCompleteCallback onWriteComplete_;\n    TcpWriteHighWaterCallback onWriteHighWater;\n\n    std::shared_ptr<void> userData_;\n};\n\n    \ntemplate <typename T>\ninline std::shared_ptr<T> Connection::GetUserData() const\n{\n    return std::static_pointer_cast<T>(userData_);\n}\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Connector.cc",
    "content": "#include <errno.h>\n\n#include <cstring>\n#include <cassert>\n#include \"EventLoop.h\"\n#include \"Connector.h\"\n#include \"Connection.h\"\n#include \"AnanasDebug.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nConnector::Connector(EventLoop* loop) :\n    loop_(loop)\n{\n}\n\nConnector::~Connector()\n{\n    if (timeoutId_)\n    {\n        bool succ = loop_->Cancel(timeoutId_);\n        assert (succ);\n    }\n\n    if (localSock_ != kInvalid)\n    {\n        if (state_ != ConnectState::connected)\n        {\n            INF(internal::g_debug) << __FUNCTION__ << localSock_ ;\n            CloseSocket(localSock_);\n        }\n    }\n}\n\nvoid Connector::SetNewConnCallback(NewTcpConnCallback cb)\n{\n    newConnCallback_ = std::move(cb);\n}\n\nvoid Connector::SetFailCallback(TcpConnFailCallback cb)\n{\n    onConnectFail_ = std::move(cb);\n}\n\nbool Connector::Connect(const SocketAddr& addr, DurationMs timeout)\n{\n    if (!addr.IsValid())\n        return false;\n\n    if (state_ != ConnectState::none)\n    {\n        INF(internal::g_debug) << \"Already connect or connecting \"\n                               << peer_.ToString();\n        return false;\n    }\n\n    assert (localSock_ == kInvalid);\n\n    peer_ = addr;\n    localSock_ = CreateTCPSocket();\n    if (localSock_ == kInvalid)\n        return false;\n\n    SetNonBlock(localSock_);\n    SetNodelay(localSock_);\n    SetRcvBuf(localSock_);\n    SetSndBuf(localSock_);\n\n    int ret = ::connect(localSock_, (struct sockaddr*)&peer_, sizeof peer_);\n    if (ret == 0)\n    {\n        INF(internal::g_debug) << \"Soon SUCCESS! client socket \" << localSock_\n                               << \" connected to \" << peer_.ToString();\n        _OnSuccess();\n        return true;\n    }\n    else\n    {\n        if (EINPROGRESS == errno)\n        {\n            INF(internal::g_debug) << \"EINPROGRESS : client socket \" << localSock_\n                                   << \" connected to \" << peer_.ToString();\n\n            state_ = ConnectState::connecting;\n            loop_->Register(eET_Write, this);\n\n            if (timeout != DurationMs::max())\n            {\n                timeoutId_ = loop_->ScheduleAfter<1>(timeout, [this]() {\n                    if (this->state_ != ConnectState::connected) {\n                        this->timeoutId_.reset();\n                        this->_OnFailed();\n                    }\n                });\n            }\n\n            return true;\n        }\n\n        _OnFailed();\n        return false;\n    }\n\n    return false; // never here\n}\n\nint Connector::Identifier() const\n{\n    return localSock_;\n}\n\nbool Connector::HandleReadEvent()\n{\n    assert (false);\n    return false;\n}\n\nbool Connector::HandleWriteEvent()\n{\n    int error = 0;\n    socklen_t len = sizeof(error);\n\n    if (::getsockopt(localSock_, SOL_SOCKET, SO_ERROR, &error, &len) < 0 ||\n                     error != 0)\n    {\n        if (error != 0)\n            errno = error;\n\n        ERR(internal::g_debug) << \"HandleWriteEvent failed: clientsocket \" << localSock_\n                               << \" connected to \" << peer_.ToString()\n                               << \", error is \" << error;\n        return false;\n    }\n        \n    _OnSuccess();\n    return true;\n}\n\nvoid Connector::HandleErrorEvent()\n{ \n    if (localSock_ == kInvalid)\n        return;\n\n    _OnFailed();\n}\n\nvoid Connector::_OnSuccess()\n{\n    assert (state_ != ConnectState::connected);\n\n    const auto oldState = state_;\n    state_  = ConnectState::connected;\n    INF(internal::g_debug) << \"Connect success! Socket \" << localSock_\n                           << \", connected to port \" << peer_.ToString();\n\n    // create new conn\n    auto c = std::make_shared<Connection>(loop_);\n    c->Init(localSock_, peer_);\n        \n    // unregister connector\n    if (oldState == ConnectState::connecting)\n        this->loop_->Unregister(eET_Write, this);\n\n    // register new conn\n    if (this->loop_->Register(eET_Read, c.get()))\n    {\n        c->SetFailCallback(this->onConnectFail_);\n        this->newConnCallback_(c.get());\n        c->_OnConnect();\n    }\n    else\n    {\n        ERR(internal::g_debug) << \"_OnSuccess but register socket \"\n                               << c->Identifier()\n                               << \" failed!\";\n    }\n}\n\nvoid Connector::_OnFailed()\n{\n    assert (state_ != ConnectState::connected);\n\n    const auto oldState = state_;\n\n    if (state_ == ConnectState::failed)\n        return;\n\n    state_ = ConnectState::failed;\n\n    INF(internal::g_debug) << \"Failed client socket \" << localSock_\n                           << \" connected to \" << peer_.ToString();\n\n    if (onConnectFail_) \n        onConnectFail_(loop_, peer_);\n\n    if (oldState == ConnectState::connecting)\n        loop_->Unregister(eET_Write, this);\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Connector.h",
    "content": "\n#ifndef BERT_CONNECTOR_H\n#define BERT_CONNECTOR_H\n\n#include \"Socket.h\"\n#include \"Timer.h\"\n#include \"Typedefs.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nenum class ConnectState\n{\n    none,\n    connecting,\n    connected,\n    failed,\n};\n\nclass Connector : public EventSource\n{\npublic:\n    explicit\n    Connector(EventLoop* loop);\n    ~Connector();\n\n    Connector(const Connector& ) = delete;\n    void operator= (const Connector& ) = delete;\n\n    void SetNewConnCallback(NewTcpConnCallback cb);\n    void SetFailCallback(TcpConnFailCallback cb);\n    bool Connect(const SocketAddr& addr, DurationMs timeout);\n\n    int Identifier() const override;\n    bool HandleReadEvent() override;\n    bool HandleWriteEvent() override;\n    void HandleErrorEvent() override;\n\n    ConnectState State() const { return state_; }\n\nprivate:\n    void _OnSuccess();\n    void _OnFailed();\n\n    int localSock_ = kInvalid;\n    SocketAddr peer_;\n    EventLoop* const loop_;\n\n    ConnectState state_ = ConnectState::none;\n\n    TimerId timeoutId_;\n\n    TcpConnFailCallback onConnectFail_;\n    NewTcpConnCallback newConnCallback_;\n};\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/DatagramSocket.cc",
    "content": "#include <errno.h>\n#include <memory>\n\n#include \"DatagramSocket.h\"\n#include \"EventLoop.h\"\n#include \"AnanasDebug.h\"\n\nnamespace ananas\n{\n\nDatagramSocket::DatagramSocket(EventLoop* loop) :\n    loop_(loop),\n    localSock_(kInvalid),\n    maxPacketSize_(2048)\n{\n}\n\nDatagramSocket::~DatagramSocket()\n{\n    INF(internal::g_debug) << \"Close Udp socket \" << Identifier();\n    CloseSocket(localSock_);\n}\n\nbool DatagramSocket::Bind(const SocketAddr* addr)\n{\n    if (localSock_ != kInvalid)\n    {\n        ERR(internal::g_debug) << \"UDP socket repeat create\";\n        return false;\n    }\n\n    localSock_ = CreateUDPSocket();\n    if (localSock_ == kInvalid)\n    {\n        ERR(internal::g_debug) << \"Failed create udp socket! Error = \" << errno;\n        return false;\n    }\n\n    SetNonBlock(localSock_);\n    SetReuseAddr(localSock_);\n\n    const bool isServer = (addr && addr->IsValid());\n    if (isServer)\n    {\n        //  server UDP\n        uint16_t port = addr->GetPort();\n        int ret = ::bind(localSock_, (struct sockaddr*)&addr->GetAddr(), sizeof(addr->GetAddr()));\n        if (kError == ret)\n        {\n            CloseSocket(localSock_);\n            ERR(internal::g_debug) << \"cannot bind udp port \" << port;\n            return false;\n        }\n    }\n    else\n    {\n        ; // nothing to do: client UDP\n    }\n            \n    if (!loop_->Register(internal::eET_Read, this))\n    {\n        ERR(internal::g_debug) << \"add udp to loop failed, socket = \" << localSock_;\n        return false;\n    }\n\n    if (onCreate_)\n        onCreate_(this);\n        \n    INF(internal::g_debug) << \"Create new udp fd = \" << localSock_;\n    return true;\n}\n\nint DatagramSocket::Identifier() const \n{\n    return localSock_;\n}\n\n    \nbool DatagramSocket::HandleReadEvent()\n{\n    std::unique_ptr<char []> recvbuf(new char[maxPacketSize_]);\n    while (true)\n    {\n        socklen_t len = sizeof srcAddr_;\n        int bytes = ::recvfrom(localSock_,\n                               &recvbuf[0], maxPacketSize_,\n                               0,\n                               (struct sockaddr*)&srcAddr_, &len);\n\n        if (kError == bytes && (EAGAIN == errno || EWOULDBLOCK == errno))\n            return true;\n\n        if (bytes <= 0)\n        {\n            ERR(internal::g_debug) << \"UDP fd \" << localSock_\n                                   << \", HandleRead error : \" << bytes \n                                   << \", errno = \" << errno;\n            return true;\n        }\n\n        // onMessage_ is void\n        onMessage_(this, &recvbuf[0], bytes);\n    }\n\n    return  true;\n}\n\nvoid DatagramSocket::_PutSendBuf(const void* data, size_t size, const SocketAddr* dst)\n{\n    Package pkg;\n    pkg.dst = *dst;\n    pkg.data.assign(reinterpret_cast<const char* >(data), size);\n\n    sendList_.emplace_back(std::move(pkg));\n}\n\nint DatagramSocket::_Send(const void* data, size_t size, const SocketAddr& dst)\n{\n    int bytes = ::sendto(localSock_,\n                         data, size,\n                         0,\n                         (const struct sockaddr*)&dst, sizeof dst);\n\n    if (bytes == kError && (EAGAIN == errno || EWOULDBLOCK == errno))\n    {\n        WRN(internal::g_debug) << \"send wouldblock\";\n        return 0;\n    }\n\n    return bytes;\n}\n\nbool DatagramSocket::SendPacket(const void* data, size_t size, const SocketAddr* dst)\n{\n    if (size == 0 || !data)\n        return true;\n\n    if (!dst)\n        dst = &srcAddr_;\n\n    if (!sendList_.empty())\n    {\n        _PutSendBuf(data, size, dst);\n        return true;\n    }\n\n    int bytes = _Send(data, size, *dst);\n    if (bytes == 0)\n    {\n        _PutSendBuf(data, size, dst);\n        loop_->Modify(internal::eET_Read | internal::eET_Write, this);\n        return true;\n    }\n    else if (bytes < 0)\n    {\n        ERR(internal::g_debug) << \"Fatal error when send udp to \"\n                               << dst->ToString()\n                               << \", must skip it\";\n        return false;\n    }\n\n\treturn true;\n}\n\nbool DatagramSocket::HandleWriteEvent()\n{\n    while (!sendList_.empty())\n    {\n        const auto& pkg = sendList_.front();\n\n        int bytes = _Send(pkg.data.data(), pkg.data.size(), pkg.dst);\n        if (bytes == 0)\n        {\n            return true;\n        }\n        else if (bytes > 0)\n        {\n            sendList_.pop_front();\n        }\n        else\n        {\n            ERR(internal::g_debug) << \"Fatal error when send udp to \"\n                                   << pkg.dst.ToString()\n                                   << \", must skip it\";\n            sendList_.pop_front();\n        }\n    }\n\n    if (sendList_.empty())\n        loop_->Modify(internal::eET_Read, this);\n        \n    return true;\n}\n\nvoid DatagramSocket::HandleErrorEvent()\n{\n    ERR(internal::g_debug) << \"HandleErrorEvent\";\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/DatagramSocket.h",
    "content": "\n#ifndef BERT_DATAGRAMSOCKET_H\n#define BERT_DATAGRAMSOCKET_H\n\n#include <list>\n#include \"Socket.h\"\n#include \"Typedefs.h\"\n#include \"Poller.h\"\n\nnamespace ananas\n{\n\nclass EventLoop;\n\nclass DatagramSocket : public internal::EventSource\n{\npublic:\n    explicit\n    DatagramSocket(EventLoop* loop);\n    ~DatagramSocket();\n\n    DatagramSocket(const DatagramSocket& ) = delete;\n    void operator= (const DatagramSocket& ) = delete;\n\n    void SetMaxPacketSize(std::size_t s);\n    bool Bind(const SocketAddr* addr);\n\n    int Identifier() const override;\n    bool HandleReadEvent() override;\n    bool HandleWriteEvent() override;\n    void HandleErrorEvent() override;\n\n    bool SendPacket(const void* , size_t , const SocketAddr* = nullptr);\n\n    const SocketAddr& PeerAddr() const { return srcAddr_; }\n\n    void SetMessageCallback(UDPMessageCallback mcb) { onMessage_ = std::move(mcb); }\n    void SetCreateCallback(UDPCreateCallback ccb) { onCreate_ = std::move(ccb); }\n\nprivate:\n    void _PutSendBuf(const void* data, size_t size, const SocketAddr* dst);\n    int _Send(const void* data, size_t size, const SocketAddr& dst);\n\n    EventLoop* const loop_;\n    int localSock_;\n    std::size_t maxPacketSize_;\n    SocketAddr srcAddr_;\n\n    struct Package\n    {\n        SocketAddr dst;\n        std::string data;\n    };\n    std::list<Package> sendList_;\n    \n    UDPMessageCallback onMessage_;\n    UDPCreateCallback onCreate_;\n};\n\n} // namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Epoller.cc",
    "content": "#ifdef __gnu_linux__\n\n#include \"Epoller.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\n#include \"AnanasDebug.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nnamespace Epoll\n{\n    bool ModSocket(int epfd, int socket, uint32_t events, void* ptr);\n\n    bool AddSocket(int epfd, int socket, uint32_t events, void* ptr)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event  ev;\n        ev.data.ptr= ptr;\n        ev.events = 0;\n\n        if (events & eET_Read)\n            ev.events |= EPOLLIN;\n        if (events & eET_Write)\n            ev.events |= EPOLLOUT;\n\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_ADD, socket, &ev);\n    }\n\n    bool DelSocket(int epfd, int socket)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event dummy;\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_DEL, socket, &dummy) ;\n    }\n\n    bool ModSocket(int epfd, int socket, uint32_t events, void* ptr)\n    {\n        if (socket < 0)\n            return false;\n\n        epoll_event  ev;\n        ev.data.ptr= ptr;\n        ev.events = 0;\n\n        if (events & eET_Read)\n            ev.events |= EPOLLIN;\n        if (events & eET_Write)\n            ev.events |= EPOLLOUT;\n\n        return 0 == epoll_ctl(epfd, EPOLL_CTL_MOD, socket, &ev);\n    }\n}\n\n\nEpoller::Epoller()\n{\n    multiplexer_ = ::epoll_create(512);\n    DBG(internal::g_debug) << \"create epoll: \" << multiplexer_;\n}\n\nEpoller::~Epoller()\n{\n    if (multiplexer_ != -1)  \n    {\n        DBG(internal::g_debug) << \"close epoll:  \" << multiplexer_;\n        ::close(multiplexer_);\n    }\n}\n\nbool Epoller::Register(int fd, int events, void* userPtr)\n{\n    if (Epoll::AddSocket(multiplexer_, fd, events, userPtr))\n        return true;\n\n    return (errno == EEXIST) && Modify(fd, events, userPtr);\n}\n    \nbool Epoller::Unregister(int fd, int events)\n{\n    return Epoll::DelSocket(multiplexer_, fd);\n}\n\n   \nbool Epoller::Modify(int fd, int events, void* userPtr)\n{\n    if (events == 0)\n        return Unregister(fd, 0);\n\n    if (Epoll::ModSocket(multiplexer_, fd, events, userPtr))\n        return  true;\n\n    return  errno == ENOENT && Register(fd, events, userPtr);\n}\n\n\nint Epoller::Poll(size_t maxEvent, int timeoutMs)\n{\n    if (maxEvent == 0)\n        return 0;\n\n    while (events_.size() < maxEvent)\n        events_.resize(2 * events_.size() + 1);\n\n    int nFired = TEMP_FAILURE_RETRY(::epoll_wait(multiplexer_, &events_[0], maxEvent, timeoutMs));\n    if (nFired == -1 && errno != EINTR && errno != EWOULDBLOCK)\n        return -1;\n\n    auto& events = firedEvents_;\n    if (nFired > 0)\n        events.resize(nFired);\n\n    for (int i = 0; i < nFired; ++ i)\n    {\n        FiredEvent& fired = events[i];\n        fired.events   = 0;\n        fired.userdata = events_[i].data.ptr;\n\n        if (events_[i].events & EPOLLIN)\n            fired.events  |= eET_Read;\n\n        if (events_[i].events & EPOLLOUT)\n            fired.events  |= eET_Write;\n\n        if (events_[i].events & (EPOLLERR | EPOLLHUP))\n            fired.events  |= eET_Error;\n    }\n\n    return nFired;\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Epoller.h",
    "content": "#ifndef BERT_EPOLLER_H\n#define BERT_EPOLLER_H\n\n#ifdef __gnu_linux__\n\n#include <sys/epoll.h>\n#include <vector>\n#include \"Poller.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nclass Epoller : public Poller\n{\npublic:\n    Epoller();\n   ~Epoller();\n\n    Epoller(const Epoller& ) = delete;\n    void operator= (const Epoller& ) = delete;\n\n    bool Register(int fd, int events, void* userPtr) override;\n    bool Modify(int fd, int events, void* userPtr) override;\n    bool Unregister(int fd, int events) override;\n\n    int Poll(std::size_t maxEvent, int timeoutMs) override;\n\nprivate:\n    std::vector<epoll_event> events_;\n};\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif // end #ifdef __gnu_linux__\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/EventLoop.cc",
    "content": "\n#include \"EventLoop.h\"\n\n#include <cassert>\n#include <thread>\n#include <signal.h>\n\n#include \"Acceptor.h\"\n#include \"Connection.h\"\n#include \"Connector.h\"\n#include \"DatagramSocket.h\"\n#include \"ThreadPool.h\"\n\n#if defined(__APPLE__)\n    #include \"Kqueue.h\"\n#elif defined(__gnu_linux__)\n    #include \"Epoller.h\"\n#else\n    #error \"Only support osx and linux\"\n#endif\n\n#include \"AnanasDebug.h\"\n#include \"AnanasLogo.h\"\n#include \"util/Util.h\"\n\nstatic void SignalHandler(int num)\n{\n    ananas::EventLoop::ExitApplication();\n}\n    \nstatic std::once_flag s_signalInit;\n\nstatic void InitSignal()\n{\n    struct sigaction sig;\n    ::memset(&sig, 0, sizeof(sig));\n   \n    sig.sa_handler = SignalHandler;\n    sigaction(SIGINT, &sig, NULL);\n                                  \n    // ignore sigpipe\n    sig.sa_handler = SIG_IGN;\n    sigaction(SIGPIPE, &sig, NULL);\n\n#ifdef ANANAS_LOGO\n    // logo\n    printf(\"%s\\n\", ananas::internal::logo);\n#endif\n}\n\n\nnamespace ananas\n{\n\n\nbool EventLoop::s_exit = false;\n    \nvoid EventLoop::ExitApplication()\n{\n    s_exit = true;\n}\n\nvoid EventLoop::SetMaxOpenFd(rlim_t maxfdPlus1)\n{\n    if (ananas::SetMaxOpenFd(maxfdPlus1))\n        s_maxOpenFdPlus1 = maxfdPlus1;\n}\n\nEventLoop::EventLoop() : stop_(false)\n{\n    internal::InitDebugLog(logALL);\n\n#if defined(__APPLE__)\n    poller_.reset(new internal::Kqueue); \n#elif defined(__gnu_linux__)\n    poller_.reset(new internal::Epoller); \n#else\n    #error \"Only support mac os and linux\"\n#endif\n}\n\nEventLoop::~EventLoop()\n{\n}\n    \n    \nbool EventLoop::Listen(const char* ip,\n                       uint16_t hostPort,\n                       NewTcpConnCallback newConnCallback)\n{\n    std::string realIp = ConvertIp(ip);\n        \n    SocketAddr addr;\n    addr.Init(realIp.c_str(), hostPort);\n\n    return Listen(addr, std::move(newConnCallback));\n}\n    \nbool EventLoop::Listen(const SocketAddr& listenAddr,\n                       NewTcpConnCallback newConnCallback)\n{\n    using internal::Acceptor;\n\n    auto s = std::make_shared<Acceptor>(this);\n    s->SetNewConnCallback(std::move(newConnCallback));\n    if (!s->Bind(listenAddr))\n        return false;\n    \n    return true;\n}\n\nbool EventLoop::ListenUDP(const SocketAddr& listenAddr,\n                          UDPMessageCallback mcb,\n                          UDPCreateCallback ccb)\n{\n    auto s = std::make_shared<DatagramSocket>(this);\n    s->SetMessageCallback(mcb);\n    s->SetCreateCallback(ccb);\n    if (!s->Bind(&listenAddr))\n        return false;\n    \n    return true;\n}\n\nbool EventLoop::ListenUDP(const char* ip, uint16_t hostPort,\n                          UDPMessageCallback mcb,\n                          UDPCreateCallback ccb)\n{\n    std::string realIp = ConvertIp(ip);\n        \n    SocketAddr addr;\n    addr.Init(realIp.c_str(), hostPort);\n\n    return ListenUDP(addr, mcb, ccb);\n}\n\n\nbool EventLoop::CreateClientUDP(UDPMessageCallback mcb,\n                                UDPCreateCallback ccb)\n{\n    auto s = std::make_shared<DatagramSocket>(this);\n    s->SetMessageCallback(mcb);\n    s->SetCreateCallback(ccb);\n    if (!s->Bind(nullptr))\n        return false;\n    \n    return true;\n}\n\nbool EventLoop::Connect(const char* ip,\n                        uint16_t hostPort,\n                        NewTcpConnCallback nccb,\n                        TcpConnFailCallback cfcb,\n                        DurationMs timeout)\n{\n    std::string realIp = ConvertIp(ip);\n        \n    SocketAddr addr;\n    addr.Init(realIp.c_str(), hostPort);\n\n    return Connect(addr, nccb, cfcb, timeout);\n}\n\n\nbool EventLoop::Connect(const SocketAddr& dst,\n                        NewTcpConnCallback nccb,\n                        TcpConnFailCallback cfcb,\n                        DurationMs timeout)\n{\n    using internal::Connector;\n\n    auto cli = std::make_shared<Connector>(this);\n    cli->SetFailCallback(cfcb);\n    cli->SetNewConnCallback(nccb);\n\n    if (!cli->Connect(dst, timeout))\n        return false;\n\n    return true;\n}\n\n\nthread_local unsigned int EventLoop::s_id = 0;\n\nrlim_t EventLoop::s_maxOpenFdPlus1 = ananas::GetMaxOpenFd();\n\nbool EventLoop::Register(int events, internal::EventSource* src)\n{\n    if (events == 0)\n        return false;\n\n    if (src->GetUniqueId() != 0)\n        assert(false);\n\n    /* man getrlimit:\n     * RLIMIT_NOFILE\n     * Specifies a value one greater than the maximum file descriptor number\n     * that can be opened by this process.\n     * Attempts (open(2), pipe(2), dup(2), etc.)  to exceed this limit yield the error EMFILE.\n     */\n    if (src->Identifier() + 1 >= static_cast<int>(s_maxOpenFdPlus1))\n    {\n        ERR(internal::g_debug)\n            << \"Register failed! Max open fd \" << s_maxOpenFdPlus1\n            << \", current fd \" << src->Identifier();\n\n        return false;\n    }\n\n    ++ s_id;\n    if (s_id == 0) // wrap around\n        s_id = 1;\n\n    src->SetUniqueId(s_id);\n\n    if (poller_->Register(src->Identifier(), events, src))\n        return eventSourceSet_.insert({src->GetUniqueId(), src->shared_from_this()}).second;\n\n    return false;\n}\n\nbool EventLoop::Modify(int events, internal::EventSource* src)\n{\n    assert (eventSourceSet_.count(src->GetUniqueId()));\n    return poller_->Modify(src->Identifier(), events, src);\n}\n\nvoid EventLoop::Unregister(int events, internal::EventSource* src)\n{\n    const int fd = src->Identifier();\n    INF(internal::g_debug) << \"Unregister socket id \" << fd;\n    poller_->Unregister(fd, events);\n\n    size_t nTask = eventSourceSet_.erase(src->GetUniqueId());\n    if (nTask != 1)\n    {\n        ERR(internal::g_debug) << \"Can not find socket id \" << fd;\n        assert (false);\n    }\n}\n\nbool EventLoop::Cancel(TimerId id)\n{\n    return timers_.Cancel(id);\n}\n\nvoid EventLoop::Run()\n{\n    std::call_once(s_signalInit, InitSignal);\n\n    while (!stop_ && !s_exit)\n    {\n        const DurationMs kDefaultPollTime(10);\n        const DurationMs kMinPollTime(1);\n\n        auto timeout = std::min(kDefaultPollTime, timers_.NearestTimer());\n        timeout = std::max(kMinPollTime, timeout);\n\n        Loop(timeout);\n    }\n        \n    for (auto& kv : eventSourceSet_)\n    {\n        poller_->Unregister(internal::eET_Read | internal::eET_Write, kv.second->Identifier());\n    }\n\n    eventSourceSet_.clear();\n    poller_.reset();\n\n    if (s_exit)\n    {\n        // thread safe exit\n        LogManager::Instance().Stop();\n        ThreadPool::Instance().JoinAll();\n    }\n}\n\nbool EventLoop::Loop(DurationMs timeout)\n{\n    ANANAS_DEFER\n    {\n        timers_.Update();\n\n        // Use tmp : if f add callback to functors_\n        decltype(functors_) tmp;\n        tmp.swap(functors_);\n\n        for (const auto& f : tmp)\n            f();\n    };\n\n    if (eventSourceSet_.empty())\n    {\n        std::this_thread::sleep_for(timeout);\n        return false;\n    }\n\n    const int ready = poller_->Poll(static_cast<int>(eventSourceSet_.size()),\n                                    static_cast<int>(timeout.count()));\n    if (ready < 0)\n        return false;\n\n    const auto& fired = poller_->GetFiredEvents();\n\n    // Consider stale event, DO NOT unregister another socket in your event handler!\n\n    std::vector<std::shared_ptr<internal::EventSource>> sources(ready);\n    for (int i = 0; i < ready; ++ i)\n    {\n        auto src = (internal::EventSource* )fired[i].userdata;\n        sources[i] = src->shared_from_this();\n\n        if (fired[i].events & internal::eET_Read) \n        {\n            if (!src->HandleReadEvent())\n            {\n                src->HandleErrorEvent();\n            }\n        }\n\n        if (fired[i].events & internal::eET_Write)\n        {\n            if (!src->HandleWriteEvent())\n            {\n                src->HandleErrorEvent();\n            }\n        }\n        \n        if (fired[i].events & internal::eET_Error)\n        {\n            ERR(internal::g_debug) << \"eET_Error for \" << src->Identifier();\n            src->HandleErrorEvent();\n        }\n    }\n\n    return ready >= 0;\n}\n\n    \nvoid EventLoop::ScheduleOnceAfter(std::chrono::milliseconds duration,\n                                  std::function<void()> f)\n{\n    ScheduleAfter<1>(duration, std::move(f));\n}\n\nvoid EventLoop::ScheduleOnce(std::function<void()> f)\n{\n    ScheduleNextTick(std::move(f));\n}\n\nFuture<void> EventLoop::Sleep(std::chrono::milliseconds dur)\n{\n    Promise<void> promise;\n    auto fut = promise.GetFuture();\n\n    ScheduleAfter(dur, [promise = std::move(promise)]() mutable {\n        promise.SetValue();\n    });\n\n    return fut;\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/EventLoop.h",
    "content": "\n#ifndef BERT_EVENTLOOP_H\n#define BERT_EVENTLOOP_H\n\n#include <map>\n#include <memory>\n#include <sys/resource.h>\n\n#include \"Timer.h\"\n#include \"Poller.h\"\n#include \"Typedefs.h\"\n#include \"util/Scheduler.h\"\n#include \"future/Future.h\"\n\nnamespace ananas\n{\n\nstruct SocketAddr;\nclass  Connection;\n\nnamespace internal\n{\nclass  Connector;\n}\n\nclass EventLoop : public Scheduler\n{\npublic:\n    EventLoop();\n    ~EventLoop();\n\n    EventLoop(const EventLoop& ) = delete;\n    void operator= (const EventLoop& ) = delete;\n    EventLoop(EventLoop&& ) = delete;\n    void operator= (EventLoop&& ) = delete;\n\n    // listener\n    bool Listen(const SocketAddr& listenAddr, NewTcpConnCallback cb);\n    bool Listen(const char* ip, uint16_t hostPort, NewTcpConnCallback cb);\n    bool ListenUDP(const SocketAddr& listenAddr,\n                   UDPMessageCallback mcb,\n                   UDPCreateCallback ccb);\n    bool ListenUDP(const char* ip,\n                   uint16_t hostPort,\n                   UDPMessageCallback mcb,\n                   UDPCreateCallback ccb);\n\n    // udp client\n    bool CreateClientUDP(UDPMessageCallback mcb,\n                         UDPCreateCallback ccb);\n\n\n    // connector \n    bool Connect(const SocketAddr& dst,\n                 NewTcpConnCallback nccb,\n                 TcpConnFailCallback cfcb,\n                 DurationMs timeout = DurationMs::max());\n    bool Connect(const char* ip,\n                 uint16_t hostPort,\n                 NewTcpConnCallback nccb,\n                 TcpConnFailCallback cfcb,\n                 DurationMs timeout = DurationMs::max());\n\n    // timer\n    template <int RepeatCount = 1, typename Duration, typename F, typename... Args>\n    TimerId ScheduleAfter(const Duration& duration, F&& f, Args&&... args);\n    bool Cancel(TimerId id);\n\n    // sleep\n    Future<void> Sleep(std::chrono::milliseconds dur);\n\n    // for future\n    void ScheduleOnceAfter(std::chrono::milliseconds duration, std::function<void ()> f) override;\n    void ScheduleOnce(std::function<void ()> f) override;\n\n    // \n    template <typename F, typename... Args>\n    void ScheduleNextTick(F&& f, Args&&... args);\n\n    bool IsStop() const { return stop_; }\n    void Stop()         { stop_ = true; }\n\n    void Run();\n    bool Loop(DurationMs timeout);\n\n    bool Register(int events, internal::EventSource* src);\n    bool Modify(int events, internal::EventSource* src);\n    void Unregister(int events, internal::EventSource* src);\n\n    std::size_t Size() const { return eventSourceSet_.size(); }\n\n    static void ExitApplication();\n\n    static void SetMaxOpenFd(rlim_t maxfdPlus1);\n\nprivate:\n    bool stop_;\n    static bool s_exit;\n\n    std::map<unsigned int, std::shared_ptr<internal::EventSource> > eventSourceSet_;\n    std::unique_ptr<internal::Poller>  poller_;\n\n    internal::TimerManager timers_;\n        \n    std::vector<std::function<void ()> > functors_;\n    \n    static thread_local unsigned int s_id;\n\n    // max open fd + 1\n    static rlim_t s_maxOpenFdPlus1;\n};\n\n\ntemplate <int RepeatCount, typename Duration, typename F, typename... Args>\nTimerId EventLoop::ScheduleAfter(const Duration& duration, F&& f, Args&&... args)\n{\n    return timers_.ScheduleAfter<RepeatCount>(duration, std::forward<F>(f), std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename... Args>\nvoid EventLoop::ScheduleNextTick(F&& f, Args&&... args)\n{\n    auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n    auto func = [temp]() { (void)temp(); };\n    functors_.emplace_back(std::move(func));\n}\n\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Kqueue.cc",
    "content": "#if defined(__APPLE__)\n\n#include \"Kqueue.h\"\n#include \"AnanasDebug.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\nnamespace ananas\n{\nnamespace internal\n{\n\nKqueue::Kqueue()\n{\n    multiplexer_ = ::kqueue();\n    INF(internal::g_debug) << \"create kqueue:  \" << multiplexer_;\n}\n\nKqueue::~Kqueue()\n{\n    INF(internal::g_debug) << \"close kqueue: \" << multiplexer_;\n    if (multiplexer_ != -1)  \n        ::close(multiplexer_);\n}\n\nbool Kqueue::Register(int sock, int events, void* userPtr)\n{\n     struct kevent change[2];\n         \n     int  cnt = 0;\n     \n     if (events & eET_Read)\n     {\n         EV_SET(change + cnt, sock, EVFILT_READ, EV_ADD, 0, 0, userPtr);\n         ++ cnt;\n     }\n                 \n     if (events & eET_Write)\n     {\n         EV_SET(change + cnt, sock, EVFILT_WRITE, EV_ADD, 0, 0, userPtr);\n         ++ cnt;\n     }\n                     \n     return kevent(multiplexer_, change, cnt, NULL, 0, NULL) != -1;\n}\n    \nbool Kqueue::Unregister(int sock, int events)\n{\n    struct kevent change[2];\n    int cnt = 0;\n\n    if (events & eET_Read)\n    {\n        EV_SET(change + cnt, sock, EVFILT_READ, EV_DELETE, 0, 0, NULL);\n        ++ cnt;\n    }\n\n    if (events & eET_Write)\n    {\n        EV_SET(change + cnt, sock, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);\n        ++ cnt;\n    }\n                    \n    if (cnt == 0)\n        return false;\n                        \n    return -1 != kevent(multiplexer_, change, cnt, NULL, 0, NULL);\n}\n\n   \nbool Kqueue::Modify(int sock, int events, void* userPtr)\n{\n    bool ret = Unregister(sock, eET_Read | eET_Write);\n    if (events == 0)\n        return ret;\n\n    return Register(sock, events, userPtr);\n}\n\n\nint Kqueue::Poll(std::size_t maxEvent, int timeoutMs)\n{\n    if (maxEvent == 0)\n        return 0;\n\n    while (events_.size() < maxEvent)\n        events_.resize(2 * events_.size() + 1);\n\n    struct timespec* pTimeout = NULL;  \n    struct timespec  timeout;\n    if (timeoutMs >= 0)\n    {\n        pTimeout = &timeout;\n        timeout.tv_sec  = timeoutMs / 1000;\n        timeout.tv_nsec = timeoutMs % 1000 * 1000000;\n    }\n\n    int nFired = ::kevent(multiplexer_, NULL, 0, &events_[0], static_cast<int>(maxEvent), pTimeout);\n    if (nFired == -1)\n        return -1;\n\n    auto& events = firedEvents_;\n    if (nFired > 0 && static_cast<size_t>(nFired) > events.size())\n        events.resize(nFired);\n\n    for (int i = 0; i < nFired; ++ i)\n    {\n        FiredEvent& fired = events[i];\n        fired.events   = 0;\n        fired.userdata = events_[i].udata;\n\n        if (events_[i].filter == EVFILT_READ)\n            fired.events  |= eET_Read;\n\n        if (events_[i].filter == EVFILT_WRITE)\n            fired.events  |= eET_Write;\n\n        if (events_[i].flags & EV_ERROR)\n            fired.events  |= eET_Error;\n    }\n\n    return nFired;\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Kqueue.h",
    "content": "#ifndef BERT_KQUEUE_H\n#define BERT_KQUEUE_H\n\n#if defined(__APPLE__)\n\n#include <vector>\n#include <sys/event.h>\n#include \"Poller.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nclass Kqueue : public Poller\n{\npublic:\n    Kqueue();\n   ~Kqueue();\n\n    bool Register(int fd, int events, void* userPtr) override;\n    bool Modify(int fd, int events, void* userPtr) override;\n    bool Unregister(int fd, int events) override;\n\n    int Poll(std::size_t maxEvent, int timeoutMs) override;\n\nprivate:\n    std::vector<struct kevent> events_;    \n};\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif  // end #if defined(__APPLE__)\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Poller.h",
    "content": "#ifndef BERT_POLLER_H\n#define BERT_POLLER_H\n\n#include <vector>\n#include <memory>\n\nnamespace ananas\n{\nnamespace internal\n{\n\nenum EventType\n{\n    eET_None  = 0,\n    eET_Read  = 0x1 << 0,\n    eET_Write = 0x1 << 1,\n    eET_Error = 0x1 << 2,\n};\n\nclass EventSource : public std::enable_shared_from_this<EventSource>\n{\npublic:\n    EventSource() { }\n    virtual ~EventSource() { }\n\n    virtual int Identifier() const = 0; // the socket \n\n    unsigned int GetUniqueId() const { return unique_id_; }\n    void SetUniqueId(unsigned int id) { unique_id_ = id; }\n\n    virtual bool HandleReadEvent() = 0;\n    virtual bool HandleWriteEvent() = 0;\n    virtual void HandleErrorEvent() = 0;\n\nprivate:\n    unsigned int unique_id_ = 0; // dispatch by ioloop\n};\n    \n\nstruct FiredEvent\n{\n    int   events;\n    void* userdata;\n\n    FiredEvent() : events(0), userdata(nullptr)\n    {\n    }\n};\n\nclass Poller\n{\npublic:\n    Poller() : multiplexer_(-1)\n    {\n    }\n\n    virtual ~Poller()\n    {\n    }\n\n    virtual bool Register(int fd, int events, void* userPtr) = 0;\n    virtual bool Modify(int fd, int events, void* userPtr) = 0;\n    virtual bool Unregister(int fd, int events) = 0;\n\n    virtual int Poll(std::size_t maxEv, int timeoutMs) = 0;\n    const std::vector<FiredEvent>& GetFiredEvents() const {  return firedEvents_; }\n\nprotected:\n    int multiplexer_;\n    std::vector<FiredEvent>  firedEvents_;\n};\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Socket.cc",
    "content": "#include <cassert>\n\n#include <fcntl.h>\n#include <netinet/tcp.h>\n#include <unistd.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n\n#include \"Socket.h\"\n\nnamespace ananas\n{\n\nconst int kInvalid = -1;\n\nconst int kTimeout =  0;\nconst int kError = -1;\nconst int kEof = -2;\n\nint CreateUDPSocket()\n{\n    return ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);\n}\n\nint CreateTCPSocket()\n{\n    return ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n}\n\nbool CreateSocketPair(int& readSock, int& writeSock)\n{\n    int s[2];\n    int ret = socketpair(AF_LOCAL, SOCK_STREAM, IPPROTO_TCP, s);\n    if (ret != 0)\n        return false;\n\n    readSock = s[0];\n    writeSock = s[1];\n\n    return true;\n}\n\nvoid CloseSocket(int& sock)\n{\n    if (sock != kInvalid)\n    {\n        ::close(sock);\n        sock = kInvalid;\n    }\n}\n\nvoid SetNonBlock(int sock, bool nonblock)\n{\n    int flag = ::fcntl(sock, F_GETFL, 0); \n    assert(flag >= 0 && \"Non Block failed\");\n\n    if (nonblock)\n        flag = ::fcntl(sock, F_SETFL, flag | O_NONBLOCK);\n    else\n        flag = ::fcntl(sock, F_SETFL, flag & ~O_NONBLOCK);\n}\n\nvoid SetNodelay(int sock, bool enable)\n{\n    int nodelay = enable ? 1 : 0;\n    ::setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(int));\n}\n\nvoid SetSndBuf(int sock, socklen_t winsize)\n{\n    ::setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&winsize, sizeof(winsize));\n}\n\nvoid SetRcvBuf(int sock, socklen_t winsize)\n{\n    ::setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&winsize, sizeof(winsize));\n}\n\nvoid SetReuseAddr(int sock)\n{\n    int reuse = 1;\n    ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));\n}\n\nbool GetLocalAddr(int sock, SocketAddr& addr)\n{\n    sockaddr_in localAddr;\n    socklen_t   len = sizeof(localAddr);\n\n    if (0 == ::getsockname(sock, (struct sockaddr*)&localAddr, &len))\n    {\n        addr.Init(localAddr);\n    }\n    else\n    {\n        return  false;\n    }\n\n    return  true;\n}\n\nbool GetPeerAddr(int sock, SocketAddr& addr)\n{\n    sockaddr_in  remoteAddr;\n    socklen_t    len = sizeof(remoteAddr);\n    if (0 == ::getpeername(sock, (struct sockaddr*)&remoteAddr, &len))\n    {\n        addr.Init(remoteAddr);\n    }\n    else\n    {\n        return  false;\n    }\n\n    return  true;\n}\n\nin_addr_t GetLocalAddrInfo()\n{\n#ifdef __APPLE__\n    // TODO can not work on mac os\n    return 0;\n#else\n    char buff[512];\n    struct ifconf conf;\n    conf.ifc_len = sizeof buff;\n    conf.ifc_buf = buff;\n\n    int sock = CreateUDPSocket();\n    ioctl(sock, SIOCGIFCONF, &conf);\n    int maxnum  = conf.ifc_len / sizeof(struct ifreq);\n    struct ifreq* ifr = conf.ifc_req;\n\n    in_addr_t result = 0;\n    for (int i = 0; i < maxnum; i++)\n    {\n        if (!ifr)\n            break;\n\n        ioctl(sock, SIOCGIFFLAGS, ifr);\n\n        if (((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP))\n        {\n            struct sockaddr_in *pAddr = (struct sockaddr_in *)(&ifr->ifr_addr);\n            result = pAddr->sin_addr.s_addr;\n            break;\n        }\n        ++ ifr;\n    }\n\n    ::close(sock);\n    return result;\n#endif\n}\n\nrlim_t GetMaxOpenFd()\n{\n    struct rlimit rlp;\n\n    if (getrlimit(RLIMIT_NOFILE, &rlp) == 0)\n        return rlp.rlim_cur;\n\n    return 0;\n}\n\nbool SetMaxOpenFd(rlim_t maxfdPlus1)\n{\n    struct rlimit rlp;\n\n    if (getrlimit(RLIMIT_NOFILE, &rlp) != 0)\n        return false;\n\n    if (maxfdPlus1 <= rlp.rlim_cur)\n        return true;\n\n    if (maxfdPlus1 > rlp.rlim_max)\n        return false;\n\n    rlp.rlim_cur = maxfdPlus1;\n\n    return setrlimit(RLIMIT_NOFILE, &rlp) == 0;\n}\n\nstd::string ConvertIp(const char* ip)\n{\n    if (strncmp(ip, \"loopback\", 8) == 0)\n        return \"127.0.0.1\";\n\n    if (strncmp(ip, \"localhost\", 9) == 0)\n    {\n        ananas::SocketAddr tmp;\n        tmp.Init(ananas::GetLocalAddrInfo(), 0);\n        return tmp.GetIP();\n    }\n\n    return ip;\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Socket.h",
    "content": "\n#ifndef BERT_SOCKET_H\n#define BERT_SOCKET_H\n\n#include <arpa/inet.h>\n#include <sys/resource.h>\n#include <string.h>\n#include <string>\n#include <memory>\n\nnamespace ananas\n{\n\nstruct SocketAddr\n{\n    static const uint16_t kInvalidPort = -1;\n\n    SocketAddr()\n    {\n        memset(&addr_, 0, sizeof addr_);\n    }\n    \n    SocketAddr(const sockaddr_in& addr)\n    {\n        Init(addr);\n    }\n\n    SocketAddr(uint32_t netip, uint16_t netport)\n    {\n        Init(netip, netport);\n    }\n\n    SocketAddr(const char* ip, uint16_t hostport)\n    {\n        Init(ip, hostport);\n    }\n\n    // ip port format:  127.0.0.1:6379\n    SocketAddr(const std::string& ipport) \n    { \n        std::string::size_type p = ipport.find_first_of(':'); \n        std::string ip = ipport.substr(0, p); \n        std::string port = ipport.substr(p + 1); \n\n        Init(ip.c_str(), static_cast<uint16_t>(std::stoi(port))); \n    } \n\n    void Init(const sockaddr_in& addr)\n    {\n        memcpy(&addr_, &addr, sizeof(addr));\n    }\n\n    void Init(uint32_t netip, uint16_t netport)\n    {\n        addr_.sin_family = AF_INET;       \n        addr_.sin_addr.s_addr = netip;       \n        addr_.sin_port   = netport;\n    }\n\n    void Init(const char* ip, uint16_t hostport)\n    {\n        addr_.sin_family = AF_INET;\n        addr_.sin_addr.s_addr = ::inet_addr(ip);\n        addr_.sin_port = htons(hostport);\n    }\n\n    const sockaddr_in& GetAddr() const\n    {\n        return addr_;\n    }\n\n    std::string GetIP() const\n    {\n        char tmp[32];\n        const char* res = inet_ntop(AF_INET, &addr_.sin_addr,\n                                    tmp, (socklen_t)(sizeof tmp));\n        return std::string(res);\n    }\n\n    uint16_t GetPort() const\n    {\n        return ntohs(addr_.sin_port);\n    }\n\n    std::string ToString() const\n    {\n        char tmp[32];\n        const char* res = inet_ntop(AF_INET, &addr_.sin_addr, tmp, (socklen_t)(sizeof tmp));\n\n        return std::string(res) + \":\" + std::to_string(ntohs(addr_.sin_port));\n    }\n\n    bool IsValid() const { return addr_.sin_family != 0; }\n\nprivate:\n    sockaddr_in  addr_;\n};\n\nextern const int kInvalid;\n\nextern const int kTimeout;\nextern const int kError;\nextern const int kEof;\n\nint CreateTCPSocket();\nint CreateUDPSocket();\nbool CreateSocketPair(int& readSock, int& writeSock);\nvoid CloseSocket(int &sock);\nvoid SetNonBlock(int sock, bool nonBlock = true);\nvoid SetNodelay(int sock, bool enable = true);\nvoid SetSndBuf(int sock, socklen_t size = 64 * 1024);\nvoid SetRcvBuf(int sock, socklen_t size = 64 * 1024);\nvoid SetReuseAddr(int sock);\nbool GetLocalAddr(int sock, SocketAddr& );\nbool GetPeerAddr(int sock, SocketAddr& );\n\nin_addr_t GetLocalAddrInfo();\n\nrlim_t GetMaxOpenFd();\nbool SetMaxOpenFd(rlim_t maxfdPlus1);\n\nstd::string ConvertIp(const char* ip);\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/ThreadPool.cc",
    "content": "#include \"ThreadPool.h\"\n\nnamespace ananas\n{\n\nthread_local bool ThreadPool::working_ = true;\nstd::thread::id ThreadPool::s_mainThread;\n\nThreadPool::ThreadPool() : currentThreads_{0}, waiters_(0), shutdown_(false)\n{\n    monitor_ = std::thread([this]() { _MonitorRoutine(); } );\n    maxIdleThreads_ = maxThreads_ = std::max(1U, std::thread::hardware_concurrency());\n    pendingStopSignal_ = 0;\n\n    // init main thread id \n    s_mainThread = std::this_thread::get_id();\n}\n\nThreadPool::~ThreadPool()\n{\n    JoinAll();\n}\n\nThreadPool& ThreadPool::Instance()\n{\n    static ThreadPool pool;\n    return pool;\n}\n\nvoid ThreadPool::SetMaxIdleThreads(unsigned int m)\n{\n    if (0 < m && m <= kMaxThreads)\n        maxIdleThreads_ = m;\n}\n\nvoid ThreadPool::SetMaxThreads(unsigned int m)\n{\n    if (0 < m && m <= kMaxThreads)\n        maxThreads_ = m;\n}\n\nvoid ThreadPool::JoinAll()\n{\n    if (s_mainThread != std::this_thread::get_id())\n        return;\n\n    decltype(workers_)  tmp;\n    \n    {\n        std::unique_lock<std::mutex>  guard(mutex_);\n        if (shutdown_)\n            return;\n        \n        shutdown_ = true;\n        cond_.notify_all();\n        \n        tmp.swap(workers_);\n    }\n    \n    for (auto& t : tmp)\n    {\n        if (t.joinable())\n            t.join();\n    }\n    \n    if (monitor_.joinable())\n    {\n        monitor_.join();\n    }\n}\n\nvoid ThreadPool::_CreateWorker()\n{\n    // guarded by mutex.\n    ++ currentThreads_;\n    std::thread t([this]() { this->_WorkerRoutine(); } );\n    workers_.push_back(std::move(t));\n}\n\nvoid ThreadPool::_WorkerRoutine()\n{\n    working_ = true;\n    \n    while (working_)\n    {\n        std::function<void ()> task;\n        \n        {\n            std::unique_lock<std::mutex> guard(mutex_);\n            \n            ++ waiters_;\n            cond_.wait(guard, [this]() { return shutdown_ || !tasks_.empty(); } );\n            -- waiters_;\n            \n            if (shutdown_ && tasks_.empty())\n            {\n                -- currentThreads_;\n                return;\n            }\n            \n            task = std::move(tasks_.front());\n            tasks_.pop_front();\n        }\n        \n        task();\n    }\n    \n    // if reach here, this thread is recycled by monitor thread\n    -- currentThreads_;\n    -- pendingStopSignal_;\n}\n\nvoid ThreadPool::_MonitorRoutine()\n{\n    while (!shutdown_)\n    {\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n        \n        std::unique_lock<std::mutex> guard(mutex_);\n        if (shutdown_)\n            return;\n\n        auto nw = waiters_;\n\n        // if there is any pending stop signal to consume waiters\n        nw -= pendingStopSignal_;\n\n        while (nw -- > maxIdleThreads_)\n        {\n            tasks_.push_back([this]() { working_ = false; });\n            cond_.notify_one();\n            ++ pendingStopSignal_;\n        }\n    }\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/ThreadPool.h",
    "content": "#ifndef BERT_THREADPOOL_H\n#define BERT_THREADPOOL_H\n\n#include <deque>\n#include <thread>\n#include <memory>\n#include <future>\n#include <atomic>\n#include <mutex>\n#include <condition_variable>\n#include \"future/Future.h\"\n\nnamespace ananas\n{\n\nclass ThreadPool final\n{\npublic:\n    ~ThreadPool();\n    \n    ThreadPool(const ThreadPool& ) = delete;\n    void operator=(const ThreadPool& ) = delete;\n    \n    static ThreadPool& Instance();\n    \n    // F return non-void\n    template <typename F, typename... Args,\n              typename = typename std::enable_if<!std::is_void<typename std::result_of<F (Args...)>::type>::value, void>::type, typename Dummy = void>\n    auto Execute(F&& f, Args&&... args) -> Future<typename std::result_of<F (Args...)>::type>;\n\n    // F return void\n    template <typename F, typename... Args,\n              typename = typename std::enable_if<std::is_void<typename std::result_of<F (Args...)>::type>::value, void>::type>\n    auto Execute(F&& f, Args&&... args) -> Future<void>;\n    \n    void JoinAll();\n    void SetMaxIdleThreads(unsigned int );\n    void SetMaxThreads(unsigned int );\n    \nprivate:\n    ThreadPool();\n    \n    void _CreateWorker();\n    void _WorkerRoutine();\n    void _MonitorRoutine();\n    \n    std::thread monitor_;\n    std::atomic<unsigned> maxThreads_;\n    std::atomic<unsigned> currentThreads_;\n    std::atomic<unsigned> maxIdleThreads_;\n    std::atomic<unsigned> pendingStopSignal_;\n    \n    static thread_local bool working_;\n    std::deque<std::thread> workers_;\n    \n    std::mutex mutex_;\n    std::condition_variable cond_;\n    unsigned waiters_;\n    bool shutdown_;\n    std::deque<std::function<void ()> > tasks_;\n    \n    static const int kMaxThreads = 1024;\n    static std::thread::id s_mainThread;\n};\n\n\n// if F return something\ntemplate <typename F, typename... Args, typename, typename >\nauto ThreadPool::Execute(F&& f, Args&&... args) -> Future<typename std::result_of<F (Args...)>::type>\n{\n    using resultType = typename std::result_of<F (Args...)>::type;\n    \n    Promise<resultType> promise;\n    auto future = promise.GetFuture();\n\n    auto innerTask = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n    auto task = [t = std::move(innerTask), promise = std::move(promise)]() mutable {\n        try {\n            promise.SetValue(Try<resultType>(t()));\n        }\n        catch(...) {\n            promise.SetException(std::current_exception());\n        }\n    };\n    \n    {\n        std::unique_lock<std::mutex> guard(mutex_);\n        if (shutdown_)\n            return MakeReadyFuture<resultType>(resultType());\n        \n        tasks_.emplace_back( [task = std::move(task)]() mutable { (task)(); } );\n        if (waiters_ == 0)\n            _CreateWorker();\n        \n        cond_.notify_one();\n    }\n    \n    return future;\n}\n\n// F return void\ntemplate <typename F, typename... Args, typename >\nauto ThreadPool::Execute(F&& f, Args&&... args) -> Future<void>\n{\n    using resultType = typename std::result_of<F (Args...)>::type;\n    static_assert(std::is_void<resultType>::value, \"must be void\");\n    \n    Promise<resultType> promise;\n    auto future = promise.GetFuture();\n\n    auto innerTask = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n    auto task = [t = std::move(innerTask), promise = std::move(promise)]() mutable {\n        try {\n            t();\n            promise.SetValue();\n        }\n        catch(...) {\n            promise.SetException(std::current_exception());\n        }\n    };\n    \n    {\n        std::unique_lock<std::mutex> guard(mutex_);\n        if (shutdown_)\n            return MakeReadyFuture();\n        \n        tasks_.emplace_back( [task = std::move(task)]() mutable { (task)(); } );\n        if (waiters_ == 0 && currentThreads_ < maxThreads_)\n        {\n            _CreateWorker();\n        }\n        \n        cond_.notify_one();\n    }\n    \n    return future;\n}\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/TimeUtil.cc",
    "content": "#include <cstring>\n#include \"TimeUtil.h\"\n\nnamespace ananas\n{\n\nTime::Time() : valid_(false)\n{\n    this->Now();\n}\n\nint64_t Time::MilliSeconds() const\n{\n    return std::chrono::duration_cast<std::chrono::milliseconds>(now_.time_since_epoch()).count();\n}\n\nint64_t Time::MicroSeconds() const\n{\n    return std::chrono::duration_cast<std::chrono::microseconds>(now_.time_since_epoch()).count();\n}\n\nvoid Time::Now()\n{\n    now_ = std::chrono::system_clock::now();\n    valid_ = false;\n}\n\nvoid Time::_UpdateTm()  const\n{\n    // lazy compute\n    if (valid_)  \n        return;\n\n    valid_ = true; \n    const time_t now(MilliSeconds() / 1000UL);\n    ::localtime_r(&now, &tm_);\n}\n\nstd::once_flag Time::init_;\n\n// from 2015 to 2029\nconst char* Time::YEAR[] = { \"2015\", \"2016\", \"2017\", \"2018\", \"2019\", \"2020\",\n    \"2021\", \"2022\", \"2023\", \"2024\", \"2025\", \"2026\", \"2027\", \"2028\", \"2029\",\n     nullptr,\n};\n\nchar Time::NUMBER[60][2] = {\"\"};\n\nvoid Time::Init() \n{\n    for (size_t i = 0; i < sizeof NUMBER / sizeof NUMBER[0]; ++ i)\n    {\n        char tmp[3]; \n        snprintf(tmp, 3, \"%02d\", static_cast<int>(i));\n        memcpy(NUMBER[i], tmp, 2);\n    }\n}\n\nstd::size_t Time::FormatTime(char* buf) const\n{\n    std::call_once(init_, &Time::Init);\n\n    _UpdateTm();\n    \n    memcpy(buf, YEAR[tm_.tm_year + 1900 - 2015], 4);\n    buf[4] = '-';\n    memcpy(buf + 5, NUMBER[tm_.tm_mon + 1], 2);\n    buf[7] = '-';\n    memcpy(buf + 8, NUMBER[tm_.tm_mday], 2);\n    buf[10] = '[';\n    memcpy(buf + 11, NUMBER[tm_.tm_hour], 2);\n    buf[13] = ':';\n    memcpy(buf + 14, NUMBER[tm_.tm_min], 2);\n    buf[16] = ':';\n    memcpy(buf + 17, NUMBER[tm_.tm_sec], 2);\n    buf[19] = '.';\n    auto msec = MicroSeconds();\n    snprintf(buf + 20, 8, \"%06d]\", static_cast<int>(msec % 1000));\n    \n    return 27;\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/TimeUtil.h",
    "content": "\n#ifndef BERT_TIMEUTIL_H\n#define BERT_TIMEUTIL_H\n\n#include <ctime>\n#include <stdint.h>\n#include <mutex>\n#include <chrono>\n\nnamespace ananas\n{\n\nclass Time\n{\npublic:\n    Time();\n    //Time(const Time& other) = delete;\n    //Time& operator= (const Time& ) = delete;\n\n    void Now();\n    int64_t MilliSeconds() const;\n    int64_t MicroSeconds() const;\n    std::size_t FormatTime(char* buf) const;\n\n    int GetYear()   const { _UpdateTm(); return tm_.tm_year + 1900;  } \n    int GetMonth()  const { _UpdateTm(); return tm_.tm_mon + 1;  } \n    int GetDay()    const { _UpdateTm(); return tm_.tm_mday; }\n    int GetHour()   const { _UpdateTm(); return tm_.tm_hour; }\n    int GetMinute() const { _UpdateTm(); return tm_.tm_min;  }\n    int GetSecond() const { _UpdateTm(); return tm_.tm_sec;  }\n\n    operator int64_t() const { return MilliSeconds(); }\n\nprivate:\n    std::chrono::system_clock::time_point now_;\n    mutable tm tm_;\n    mutable bool valid_;\n\n    void _UpdateTm() const;\n\n    // for FormatTime \n    static std::once_flag init_;\n    static void Init();\n    static const char* YEAR[];\n    static char NUMBER[60][2];\n};\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Timer.cc",
    "content": "#include <vector>\n#include <cassert>\n#include \"Timer.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nunsigned int TimerManager::s_timerIdGen_ = 0;\n\nTimerManager::TimerManager()\n{\n}\n\nTimerManager::~TimerManager()\n{\n}\n\nvoid TimerManager::Update()\n{\n    if (timers_.empty())\n        return;\n\n    const auto now = std::chrono::steady_clock::now();\n\n    for (auto it(timers_.begin()); it != timers_.end(); ) \n    { \n        if (it->first > now) \n            return;\n                 \n        // support cancel self\n        it->second.OnTimer();\n\n        // steal and erase it\n        Timer timer(std::move(it->second));\n        it = timers_.erase(it);\n\n        if (timer.count_ != 0)\n        {\n            // need reschedule \n            const auto tp = timer.id_->first;\n            auto itNew = timers_.insert(std::make_pair(tp, std::move(timer)));\n            if (it == timers_.end() || itNew->first < it->first)\n                it = itNew;\n        }\n    } \n}\n\nbool TimerManager::Cancel(TimerId id)\n{\n    //time point + uid\n    auto begin = timers_.lower_bound(id->first);\n    if (begin == timers_.end())\n        return false;\n\n    auto end = timers_.upper_bound(id->first);\n    for (auto it(begin); it != end; ++ it)\n    {\n        if (it->second.UniqueId() == id->second)\n        {\n            // lazy delete, in case cancel another timer during on timer call.\n            it->second.count_ = 0;\n            return true;\n        }\n    }\n            \n    return false;\n}\n\nDurationMs TimerManager::NearestTimer() const\n{\n    if (timers_.empty())\n        return DurationMs::max();\n\n    const auto& timer = timers_.begin()->second;\n    auto now = std::chrono::steady_clock::now();\n    if (now > timer.Id()->first)\n        return DurationMs::min();\n    else\n        return std::chrono::duration_cast<DurationMs>(timer.Id()->first - now);\n}\n\nTimerManager::Timer::Timer(const TimePoint& tp) :\n    id_(new std::pair<TimePoint, unsigned int>{tp, ++ TimerManager::s_timerIdGen_}),\n    count_(kForever)\n{\n}\n\nTimerManager::Timer::Timer(Timer&& timer)\n{\n    _Move(std::move(timer));\n}\n\nTimerManager::Timer& TimerManager::Timer::operator= (Timer&& timer)\n{\n    if (this != &timer)\n        _Move(std::move(timer));\n\n    return *this;\n}\n\nvoid TimerManager::Timer::_Move(Timer&& timer)\n{\n    this->id_ = std::move(timer.id_);\n    this->func_ = std::move(timer.func_);\n    this->interval_ = std::move(timer.interval_);\n    this->count_ = timer.count_;\n}\n\n\nvoid TimerManager::Timer::OnTimer()\n{\n    if (!func_ || count_ == 0)\n        return;\n\n    if (count_ == kForever || count_-- > 0)\n    {\n        func_();\n        id_->first += interval_;\n    }\n    else\n    {\n        count_ = 0; // in case if count_ other than -1\n    }\n}\n\nTimerId TimerManager::Timer::Id() const\n{\n    return id_;\n}\n\nunsigned int TimerManager::Timer::UniqueId() const\n{\n    return id_->second;\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/Timer.h",
    "content": "\n#ifndef BERT_TIMERMANAGER_H\n#define BERT_TIMERMANAGER_H\n\n#include <map>\n#include <chrono>\n#include <functional>\n#include <memory>\n#include <ostream>\n\nnamespace ananas\n{\n\nusing DurationMs = std::chrono::milliseconds;\nusing TimePoint = std::chrono::steady_clock::time_point;\nusing TimerId = std::shared_ptr<std::pair<TimePoint, unsigned int> >;\n\nconstexpr int kForever = -1;\n\ninline std::ostream& operator<< (std::ostream& os, const TimerId& d)\n{\n    os << \"[TimerId:\" << (void*)d.get() << \"]\";\n    return os;\n}\n\nnamespace internal\n{\n\nclass TimerManager final\n{\npublic:\n    TimerManager();\n   ~TimerManager();\n\n    TimerManager(const TimerManager& ) = delete;\n    void operator= (const TimerManager& ) = delete;\n\n    // Tick\n    void Update();\n\n    // Schedule timer at absolute timepoint\n    template <int RepeatCount = 1/* -1 is forever */, typename F, typename... Args>\n    TimerId ScheduleAt(const TimePoint& triggerTime, F&& f, Args&&... args); // TODO ScheduleAt\n\n    // Schedule timer after some time duration\n    template <int RepeatCount = 1, typename Duration, typename F, typename... Args>\n    TimerId ScheduleAfter(const Duration& duration, F&& f, Args&&... args);\n\n    // Cancel timer\n    bool Cancel(TimerId id);\n\n    // how far the nearest timer will be trigger.\n    DurationMs NearestTimer() const;\n\nprivate:\n    class Timer\n    {\n        friend class TimerManager;\n    public:\n        explicit\n        Timer(const TimePoint& tp);\n\n        // only move\n        Timer(Timer&& timer);\n        Timer& operator= (Timer&& );\n\n        Timer(const Timer& ) = delete;\n        void operator= (const Timer& ) = delete;\n\n        template <typename F, typename... Args>\n        void SetCallback(F&& f, Args&&... args);\n\n        void OnTimer();\n\n        TimerId Id() const;\n        unsigned int UniqueId() const;\n\n    private:\n        void _Move(Timer&& timer);\n\n        TimerId id_;\n\n        std::function<void ()> func_;\n        DurationMs interval_;\n        int count_;\n    };\n\n    std::multimap<TimePoint, Timer> timers_;\n\n    TimePoint now_;\n\n    friend class Timer;\n    // not thread-safe, but who cares?\n    static unsigned int s_timerIdGen_;\n};\n\n\ntemplate <int RepeatCount, typename F, typename... Args>\nTimerId TimerManager::ScheduleAt(const TimePoint& triggerTime, F&& f, Args&&... args)\n{\n    static_assert(RepeatCount != 0, \"Why you add a timer with zero count?\");\n\n    using namespace std::chrono;\n\n    Timer t(triggerTime);\n    // precision: milliseconds\n    t.interval_ = std::max(DurationMs(1), duration_cast<DurationMs>(triggerTime - now_));\n    t.count_ = RepeatCount;\n    TimerId id = t.Id();\n\n    t.SetCallback(std::forward<F>(f), std::forward<Args>(args)...);\n    timers_.insert(std::make_pair(triggerTime, std::move(t)));\n    return id;\n}\n\ntemplate <int RepeatCount, typename Duration, typename F, typename... Args>\nTimerId TimerManager::ScheduleAfter(const Duration& duration, F&& f, Args&&... args)\n{\n    this->now_ = std::chrono::steady_clock::now();\n    return ScheduleAt<RepeatCount>(now_ + duration,\n                                   std::forward<F>(f),\n                                   std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename... Args>\nvoid TimerManager::Timer::SetCallback(F&& f, Args&&... args)\n{\n    auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n    func_ = [temp]() mutable { (void)temp(); };\n}\n\n} // end namespace internal\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/Typedefs.h",
    "content": "#ifndef BERT_TYPEDEFS_H\n#define BERT_TYPEDEFS_H\n\n#include <functional>\n\nnamespace ananas\n{\n    typedef size_t PacketLen_t;\n\n    struct SocketAddr;\n    class Connection;\n    class DatagramSocket;\n    class EventLoop;\n\n    using NewTcpConnCallback = std::function<void (Connection* )>;\n    using TcpConnFailCallback = std::function<void (EventLoop*, const SocketAddr& peer)>;\n    using TcpMessageCallback = std::function<PacketLen_t (Connection* , const char* data, PacketLen_t len)>;\n    using TcpWriteCompleteCallback = std::function<void (Connection* )>;\n    using TcpWriteHighWaterCallback = std::function<void (Connection* , size_t toSend)>;\n\n    using UDPMessageCallback = std::function<void (DatagramSocket* , const char* data, size_t len)>;\n    using UDPCreateCallback = std::function<void (DatagramSocket* )>;\n\n    using SocketPairCreateCallback = std::function<void (Connection* r, Connection* w)>;\n}\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/log/Logger.cc",
    "content": "\n#include <iostream>\n#include <cassert>\n#include <cstdlib>\n#include <cstdio>\n#include <sstream>\n#include <errno.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include \"../ThreadPool.h\"\n#include \"../TimeUtil.h\"\n#include \"Logger.h\"\n\nnamespace ananas\n{\n\nnamespace\n{\n\nenum LogColor\n{\n    Red = 1,\n    Green,\n    Yellow,\n    Normal,\n    Blue,\n    Purple,\n    White,\n    Max,\n};\n\n}\n\n// TODO config\nstatic const size_t kDefaultLogSize = 32 * 1024 * 1024;\n\nstatic const size_t kPrefixLevelLen = 6;\nstatic const size_t kPrefixTimeLen = 27;\n\nstatic bool MakeDir(const char* dir)\n{\n    if (mkdir(dir, 0755) != 0)\n    {\n        if (EEXIST != errno)\n        {\n            perror(\"MakeDir failed:\");\n            return false;\n        }\n    }\n    \n    return true;\n}\n\nthread_local char Logger::tmpBuffer_[Logger::kMaxCharPerLog];\nthread_local std::size_t Logger::pos_ = kPrefixLevelLen + kPrefixTimeLen;\nthread_local int64_t Logger::lastLogSecond_ = -1;\nthread_local int64_t Logger::lastLogMSecond_ = -1;\nthread_local unsigned int Logger::curLevel_ = 0;\nthread_local char Logger::tid_[16] = \"\";\nthread_local int Logger::tidLen_ = 0;\n\nunsigned int Logger::seq_ = 0;\n\nLogger::Logger() : shutdown_(false),\n                   level_(0),\n                   dest_(0)\n{\n    _Reset();\n}\n\nLogger::~Logger()\n{\n    _CloseLogFile();\n}\n\nbool Logger::Init(unsigned int level, unsigned int dest, const char* dir)\n{\n    level_      = level;\n    dest_       = dest;\n    directory_  = dir ? dir : \".\";\n    if (directory_.back() == '/')\n        directory_.pop_back();\n\n    if (0 == level_) \n        return  true;\n  \n    if (dest_ & logFile)\n    {\n        return directory_ == \".\" ||\n               MakeDir(directory_.c_str());\n    }\n\n    if (!(dest_ & logConsole))\n    {\n        std::cerr << \"log has no output, but loglevel is \" << level << std::endl;\n        return false;\n    }\n            \n    return true;\n}\n\nbool Logger::_CheckChangeFile()\n{\n    if (!file_.IsOpen())\n        return true;\n    \n    return file_.Offset() + kMaxCharPerLog > kDefaultLogSize;\n}\n\nconst std::string& Logger::_MakeFileName()\n{ \n    char name[32]; \n    Time now; \n    size_t len = now.FormatTime(name); \n    name[len] = '\\0';  \n\n    std::ostringstream pid;\n    pid << \"@\" << ::getpid() << \"-\"; \n\n    seq_ ++; \n    fileName_  = directory_ + \"/\" + name + pid.str() + std::to_string(seq_) + \".log\";\n            \n    return fileName_; \n}\n\nbool Logger::_OpenLogFile(const std::string& name)\n{ \n    return file_.Open(name.data(), true);\n}\n\nvoid Logger::_CloseLogFile()\n{\n    return file_.Close();\n}\n\n// TODO config\nstatic const int kFlushThreshold = 2 * 1024 * 1024;\n\nvoid Logger::Flush(enum LogLevel level)\n{\n    assert (level == curLevel_);\n\n    if (IsLevelForbid(curLevel_))\n    {\n        _Reset();\n        return;\n    }\n\n    if (!(level & curLevel_) ||\n         (pos_ < kPrefixTimeLen + kPrefixLevelLen))\n    {\n        assert (false);\n        return;\n    }\n         \n    if (pos_ == kPrefixTimeLen + kPrefixLevelLen)\n        return; // empty log\n    \n    Time now;\n\n    auto seconds = now.MilliSeconds() / 1000;\n    if (seconds != lastLogSecond_)\n    {\n        now.FormatTime(tmpBuffer_);\n        lastLogSecond_ = seconds;\n    }\n    else\n    {\n        auto msec = now.MicroSeconds() % 1000000;\n        if (msec != lastLogMSecond_)\n        {\n            snprintf(tmpBuffer_ + 20, 7, \"%06d\", static_cast<int>(msec));\n            tmpBuffer_[26] = ']';\n            lastLogMSecond_ = msec;\n        }\n    }\n\n    switch(level)\n    {\n    case logINFO:\n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[INF]:\", kPrefixLevelLen);\n        break;\n\n    case logDEBUG:\n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[DBG]:\", kPrefixLevelLen);\n        break;\n\n    case logWARN:\n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[WRN]:\", kPrefixLevelLen);\n        break;\n\n    case logERROR:\n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[ERR]:\", kPrefixLevelLen);\n        break;\n\n    case logUSR:\n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[USR]:\", kPrefixLevelLen);\n        break;\n\n    default:    \n        memcpy(tmpBuffer_ + kPrefixTimeLen, \"[???]:\", kPrefixLevelLen);\n        break;\n    }\n\n    if (tidLen_ == 0)\n    {\n        std::ostringstream oss;\n        oss << std::this_thread::get_id();\n\n        const auto& str = oss.str();\n        tidLen_ = std::min<int>(str.size(), sizeof tid_);\n        tid_[0] = '|'; // | thread_id\n        memcpy(tid_ + 1, str.data(), tidLen_);\n        tidLen_ += 1;\n    }\n\n    // Put tid_ at tail, because tid_ len vary from different platforms\n    memcpy(tmpBuffer_ + pos_, tid_, tidLen_);\n    pos_ += tidLen_;\n\n    tmpBuffer_[pos_ ++] = '\\n';\n    tmpBuffer_[pos_] = '\\0';\n\n    BufferInfo* info = nullptr;\n    {\n        std::unique_lock<std::mutex> guard(mutex_);\n        if (shutdown_)\n        {\n            std::cout << tmpBuffer_;\n            return;\n        }\n\n        info = buffers_[std::this_thread::get_id()].get();\n        if (!info)\n            buffers_[std::this_thread::get_id()].reset(info = new BufferInfo());\n\n        assert (!info->inuse_);\n        info->inuse_ = true;\n    }\n\n    // Format: level info, length, log msg\n    int logLevel = level;\n\n    info->buffer_.PushData(&logLevel, sizeof logLevel);\n    info->buffer_.PushData(&pos_, sizeof pos_);\n    info->buffer_.PushData(tmpBuffer_, pos_);\n\n    _Reset();\n\n    if (info->buffer_.ReadableSize() > kFlushThreshold)\n    {\n        info->inuse_ = false;\n        LogManager::Instance().AddBusyLog(this);\n    }\n    else\n    {\n        info->inuse_ = false;\n    }\n}\n\nvoid Logger::_Color(unsigned int color)\n{\n#if defined(__gnu_linux__)\n    const char* colorstrings[Max] = {\n        \"\",\n        \"\\033[1;31;40m\",\n        \"\\033[1;32;40m\",\n        \"\\033[1;33;40m\",\n        \"\\033[0m\",\n        \"\\033[1;34;40m\",\n        \"\\033[1;35;40m\",\n        \"\\033[1;37;40m\",\n    };\n\n    fprintf(stdout, \"%s\", colorstrings[color]);\n#endif\n}\n\nLogger&  Logger::operator<< (const char* msg)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    const auto len = strlen(msg);\n    if (pos_ + len >= kMaxCharPerLog)\n        return *this;\n\n    memcpy(tmpBuffer_ + pos_, msg, len);\n    pos_ += len;\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (const unsigned char* msg)\n{\n    return operator<<(reinterpret_cast<const char*>(msg));\n}\n\nLogger&  Logger::operator<< (const std::string& msg)\n{\n    return operator<<(msg.c_str());\n}\n\nLogger&  Logger::operator<< (void* ptr)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n    \n    if (pos_ + 18 < kMaxCharPerLog)\n    {\n        unsigned long ptrValue = (unsigned long)ptr;\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%#018lx\", ptrValue);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\n\nLogger&  Logger::operator<< (unsigned char a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 3 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%hhd\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\n\nLogger&  Logger::operator<< (char a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 3 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%hhu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned short a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 5 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%hu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (short a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 5 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%hd\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned int a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 10 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%u\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (int a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 10 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%d\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned long a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 20 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%lu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (long a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 20 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%ld\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (unsigned long long a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 20 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%llu\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (long long a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 20 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%lld\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n\n    return  *this;\n}\n\nLogger&  Logger::operator<< (double a)\n{\n    if (IsLevelForbid(curLevel_))\n        return *this;\n\n    if (pos_ + 20 < kMaxCharPerLog)\n    {\n        auto nbytes = snprintf(tmpBuffer_ + pos_, kMaxCharPerLog - pos_, \"%.6g\", a);\n        if (nbytes > 0) pos_ += nbytes;\n    }\n    \n    return  *this;\n}\n\n\nbool Logger::Update()\n{\n    std::vector<std::unique_ptr<BufferInfo> > tmpBufs;\n\n    bool todo = false;\n    {\n        std::unique_lock<std::mutex> guard(mutex_);\n\n        for (auto it(buffers_.begin()); it != buffers_.end(); )\n        {\n            assert (it->second);\n\n            if (it->second->inuse_)\n            {\n                // if logs is still in producing, there will be some work to do.\n                todo = true;\n                ++ it;\n            }\n            else\n            {\n                tmpBufs.push_back(std::move(it->second));\n                it = buffers_.erase(it);\n            }\n        }\n    }\n\n    for (auto& pbuf : tmpBufs)\n    {\n        auto nWritten = _Log(pbuf->buffer_.ReadAddr(), pbuf->buffer_.ReadableSize());\n        assert (nWritten == pbuf->buffer_.ReadableSize());\n    }\n\n    file_.Sync();\n\n    return todo;\n}\n\nvoid   Logger::_Reset()\n{\n    curLevel_ = 0;\n    pos_  = kPrefixLevelLen + kPrefixTimeLen ;\n}\n\nsize_t  Logger::_Log(const char* data, size_t dataLen)\n{\n    const auto minLogSize = sizeof(int) + sizeof(size_t);\n\n    size_t nOffset = 0;\n    while (nOffset + minLogSize < dataLen)\n    {\n        int level = *(int*)(data + nOffset);\n        size_t len = *(size_t* )(data + nOffset + sizeof(int));\n        if (dataLen < nOffset + minLogSize + len)\n        {\n            std::cerr << \"_WriteLog skip 0!!!\\n \";\n            break;\n        }\n\n        _WriteLog(level, len, data + nOffset + minLogSize);\n        nOffset += minLogSize + len;\n    }\n\n    return nOffset;\n}\n\n\nvoid Logger::_WriteLog(int level, size_t len, const char* data)\n{\n    assert (len > 0 && data);\n    \n    if (dest_ & logConsole)\n    {\n        switch (level)\n        {\n        case logINFO:\n            _Color(Green);\n            break;\n\n        case logDEBUG:\n            _Color(White);\n            break;\n\n        case logWARN:\n            _Color(Yellow);\n            break;\n\n        case logERROR:\n            _Color(Red);\n            break;\n\n        case logUSR:\n            _Color(Purple);\n            break;\n\n        default:\n            _Color(Red);\n            break;\n        }\n\n        fprintf(stdout, \"%.*s\", static_cast<int>(len), data);\n        _Color(Normal);\n    }\n\n    if (dest_ & logFile)\n    {\n        while (_CheckChangeFile())\n        {\n            _CloseLogFile();\n            if (!_OpenLogFile(_MakeFileName().c_str()))\n                break;\n        }\n\n        assert (file_.IsOpen());\n        file_.Write(data, len);\n    }\n}\n\nvoid Logger::Shutdown()\n{\n    std::unique_lock<std::mutex> guard(mutex_);\n    if (shutdown_)\n        return;\n\n    shutdown_ = true;\n    std::cout << \"stop logger \" << (void*)this << std::endl;\n}\n\nLogManager& LogManager::Instance()\n{\n    static LogManager mgr;\n    return mgr;\n}\n\nLogManager::LogManager() : shutdown_(true)\n{\n    nullLog_.Init(0);\n}\n\nvoid LogManager::Start()\n{\n    std::unique_lock<std::mutex> guard(mutex_);\n    assert (shutdown_);\n    shutdown_ = false;\n\n    ThreadPool::Instance().Execute(std::bind(&LogManager::Run, this));\n}\n\nvoid LogManager::Stop()\n{\n    {\n        std::unique_lock<std::mutex> guard(mutex_);\n        if (shutdown_)\n            return;\n\n        shutdown_ = true;\n        cond_.notify_all();\n    }\n        \n    std::lock_guard<std::mutex> guard(logsMutex_);\n    for (auto& plog : logs_)\n        plog->Shutdown();\n}\n\nstd::shared_ptr<Logger> LogManager::CreateLog(unsigned int level,\n                              unsigned int dest,\n                              const char* dir)\n{\n\n    auto log(std::make_shared<Logger>());\n            \n    if (!log->Init(level, dest, dir))\n    {   \n        std::shared_ptr<Logger> nulllog(&nullLog_, [](Logger* ) {});\n        return nulllog;\n    }   \n    else\n    {  \n        std::lock_guard<std::mutex> guard(logsMutex_);\n        if (shutdown_)\n        {\n            std::cerr << \"Warning: Please call LogManager::Start() first\\n\";\n            std::shared_ptr<Logger> nulllog(&nullLog_, [](Logger* ) {});\n            return nulllog;\n        }\n\n        logs_.emplace_back(log);\n    }   \n\n    return log;\n}\n\n    \nvoid LogManager::AddBusyLog(Logger* log)\n{\n    std::unique_lock<std::mutex> guard(mutex_);\n    if (busyLogs_.insert(log).second)\n        cond_.notify_one();\n}\n\n\nvoid LogManager::Run()\n{\n    const std::chrono::milliseconds kFlushInterval(1);\n\n    bool run = true;\n    while (run)\n    {\n        std::vector<Logger* > tmpBusy;\n\n        {\n            std::unique_lock<std::mutex> guard(mutex_);\n            cond_.wait_for(guard, kFlushInterval); \n            if (!busyLogs_.empty())\n            {\n                tmpBusy.assign(busyLogs_.begin(), busyLogs_.end());\n                busyLogs_.clear();\n            }\n\n            if (shutdown_)\n                run = false;\n        }\n\n        if (tmpBusy.empty())\n        {\n            std::unique_lock<std::mutex> guard(logsMutex_);\n            for (auto& plog : logs_)\n                tmpBusy.push_back(plog.get());\n        }\n\n        for (auto plog : tmpBusy)\n        {\n            plog->Update();\n        }\n    }\n\n    std::unique_lock<std::mutex> guard(logsMutex_);\n    assert (shutdown_);\n    while (!logs_.empty())\n    {\n        for (auto it(logs_.begin()); it != logs_.end(); )\n        {\n            if (!(*it)->Update())\n                it = logs_.erase(it);\n            else\n                ++ it;\n        }\n    }\n}\n\nLogHelper::LogHelper(LogLevel level) : level_(level) \n{\n} \n\nLogger& LogHelper::operator=(Logger& log)\n{\n    log.Flush(level_);\n    return log;\n}\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/log/Logger.h",
    "content": "#ifndef BERT_LOGGER_H\n#define BERT_LOGGER_H\n\n/*\n * A multi-thread Logger class\n */\n\n#include <string>\n#include <vector>\n#include <map>\n#include <set>\n#include <memory>\n#include <mutex>\n#include <condition_variable>\n#include <thread>\n#include <atomic>\n\n#include \"../Buffer.h\"\n#include \"MmapFile.h\"\n\nenum LogLevel\n{\n    logINFO     = 0x01 << 0,\n    logDEBUG    = 0x01 << 1,\n    logWARN     = 0x01 << 2,\n    logERROR    = 0x01 << 3,\n    logUSR      = 0x01 << 4,\n    logALL      = 0xFFFFFFFF,\n};\n\nenum LogDest\n{\n    logConsole  = 0x01 << 0,\n    logFile     = 0x01 << 1,\n    logSocket   = 0x01 << 2,\n};\n\nnamespace ananas\n{\n\nclass Logger\n{\npublic:\n    friend class LogManager;\n\n    Logger();\n   ~Logger();\n\n    Logger(const Logger& ) = delete;\n    void operator= (const Logger& ) = delete;\n    Logger(Logger&& ) = delete;\n    void operator= (Logger&& ) = delete;\n\n    bool Init(unsigned int level = logDEBUG,\n              unsigned int dest = logConsole,\n              const char* pDir  = 0);\n    \n    void Flush(LogLevel  level);\n    bool IsLevelForbid(unsigned int level) const { return  !(level & level_); };\n\n    Logger&  operator<<(const char* msg);\n    Logger&  operator<<(const unsigned char* msg);\n    Logger&  operator<<(const std::string& msg);\n    Logger&  operator<<(void* );\n    Logger&  operator<<(unsigned char a);\n    Logger&  operator<<(char a);\n    Logger&  operator<<(unsigned short a);\n    Logger&  operator<<(short a);\n    Logger&  operator<<(unsigned int a);\n    Logger&  operator<<(int a);\n    Logger&  operator<<(unsigned long a);\n    Logger&  operator<<(long a);\n    Logger&  operator<<(unsigned long long a);\n    Logger&  operator<<(long long a);\n    Logger&  operator<<(double a);\n\n    Logger& SetCurLevel(unsigned int level) {\n        curLevel_ = level;\n        return *this;\n    }\n\n    void Start();\n    void Shutdown();\n\n    bool Update();\n\nprivate:\n\n    static const size_t kMaxCharPerLog = 2048;\n    // parallel format log string\n    static thread_local char tmpBuffer_[kMaxCharPerLog];\n    static thread_local std::size_t pos_;\n    static thread_local int64_t lastLogSecond_;\n    static thread_local int64_t lastLogMSecond_;\n    static thread_local unsigned int curLevel_;\n    static thread_local char tid_[16];\n    static thread_local int tidLen_;\n\n    struct BufferInfo\n    {\n        std::atomic<bool> inuse_{false};\n        Buffer buffer_;\n    };\n\n    std::mutex mutex_;\n    std::map<std::thread::id, std::unique_ptr<BufferInfo> > buffers_;\n    bool shutdown_;\n    \n    // const vars from init()\n    unsigned int level_;\n    std::string directory_;\n    unsigned int dest_;\n    std::string fileName_;\n\n    internal::OMmapFile file_;\n    \n    std::size_t _Log(const char* data, std::size_t len);\n\n    bool _CheckChangeFile();\n    const std::string& _MakeFileName();\n    bool _OpenLogFile(const std::string& name);\n    void _CloseLogFile();\n    void _WriteLog(int level, std::size_t nLen, const char* data);\n    void _Color(unsigned int color);\n    void _Reset();\n\n    static unsigned int seq_;\n};\n\n\nclass LogManager\n{\npublic:\n    static LogManager& Instance();\n\n    void Start();\n    void Stop();\n\n    std::shared_ptr<Logger> CreateLog(unsigned int level,\n                                      unsigned int dest,\n                                      const char* dir = nullptr);\n\n    void AddBusyLog(Logger* );\n    Logger* NullLog()  {  return  &nullLog_;  }\n\nprivate:\n    LogManager();\n\n    void Run();\n\n    std::mutex logsMutex_;\n    std::vector<std::shared_ptr<Logger>> logs_;\n\n    std::mutex mutex_;\n    std::condition_variable cond_;\n    bool shutdown_;\n    std::set<Logger* > busyLogs_;\n\n    // null object\n    Logger nullLog_;\n};\n\n\nclass LogHelper\n{\npublic:\n    LogHelper(LogLevel level);\n    Logger& operator=(Logger& log);\n\nprivate:\n    LogLevel level_;\n};\n\n\n#undef INF\n#undef DBG\n#undef WRN\n#undef ERR\n#undef USR\n\n#define LOG_DBG(x) (!(x) || (x)->IsLevelForbid(logDEBUG)) ? *ananas::LogManager::Instance().NullLog() : (ananas::LogHelper(logDEBUG)) = x->SetCurLevel(logDEBUG)\n \n#define LOG_INF(x) (!(x) || (x)->IsLevelForbid(logINFO)) ? *ananas::LogManager::Instance().NullLog() : (ananas::LogHelper(logINFO)) = x->SetCurLevel(logINFO)\n\n#define LOG_WRN(x) (!(x) || (x)->IsLevelForbid(logWARN)) ? *ananas::LogManager::Instance().NullLog() : (ananas::LogHelper(logWARN)) = x->SetCurLevel(logWARN)\n\n#define LOG_ERR(x) (!(x) || (x)->IsLevelForbid(logERROR)) ? *ananas::LogManager::Instance().NullLog() : (ananas::LogHelper(logERROR)) = x->SetCurLevel(logERROR)\n\n#define LOG_USR(x) (!(x) || (x)->IsLevelForbid(logUSR)) ? *ananas::LogManager::Instance().NullLog() : (ananas::LogHelper(logUSR)) = x->SetCurLevel(logUSR)\n\n#define  DBG      LOG_DBG\n#define  INF      LOG_INF\n#define  WRN      LOG_WRN\n#define  ERR      LOG_ERR\n#define  USR      LOG_USR\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/log/MmapFile.cc",
    "content": "#include <errno.h>\n#include <fcntl.h>\n#include <assert.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <iostream>\n#include <unistd.h>\n\n\n#include \"MmapFile.h\"\n\nnamespace ananas\n{\nnamespace internal\n{\n\nstatic const size_t kDefaultSize = 1 * 1024 * 1024;\n\nstatic const int   kInvalidFile = -1;\nstatic char* const kInvalidAddr = reinterpret_cast<char*>(-1);\n\n\n// OMmapFile\nOMmapFile::OMmapFile() : file_(kInvalidFile),\n    memory_(kInvalidAddr),\n    offset_(0),\n    size_(0),\n    syncPos_(0)\n{\n}\n\nOMmapFile::~OMmapFile()\n{\n    Close();\n}\n\nvoid OMmapFile::_ExtendFileSize(size_t size)\n{\n    assert(file_ != kInvalidFile);\n\n    if (size > size_)\n        Truncate(size);\n}\n\nbool OMmapFile::Open(const std::string& file, bool bAppend)\n{\n    return Open(file.c_str(), bAppend);\n}\n\nbool OMmapFile::Open(const char* file, bool bAppend)\n{\n    Close();\n\n    file_ = ::open(file, O_RDWR | O_CREAT | (bAppend ? O_APPEND : 0), 0644);\n\n    if (file_ == kInvalidFile)\n    {\n        char err[128];\n        snprintf(err, sizeof err - 1, \"OpenWriteOnly %s failed\\n\", file);\n        perror(err);\n        assert (false);\n        return false;\n    }\n\n    if (bAppend)\n    {\n        struct stat st;\n        fstat(file_, &st);\n        size_ = std::max<size_t>(kDefaultSize, st.st_size);\n        offset_ = st.st_size;\n    }\n    else\n    {\n        size_ = kDefaultSize;\n        offset_ = 0;\n    }\n\n    int ret = ::ftruncate(file_, size_);\n    assert (ret == 0);\n\n    return _MapWriteOnly();\n}\n\nvoid  OMmapFile::Close()\n{\n    if (file_ != kInvalidFile)\n    {\n        ::munmap(memory_, size_);\n        ::ftruncate(file_, offset_);\n        ::close(file_);\n\n        file_ = kInvalidFile;\n        size_ = 0;\n        memory_ = kInvalidAddr;\n        offset_ = 0;\n        syncPos_ = 0;\n    }\n}\n\nbool    OMmapFile::Sync()\n{\n    if (file_ == kInvalidFile)\n        return false;\n\n    if (syncPos_ >= offset_)\n        return false;\n\n    ::msync(memory_ + syncPos_, offset_ - syncPos_, MS_SYNC);\n    syncPos_ = offset_;\n    \n    return true;\n}\n\nbool OMmapFile::_MapWriteOnly()\n{\n    if (size_ == 0 || file_ == kInvalidFile)\n    {\n        assert (false);\n        return false;\n    }\n\n    memory_ = (char*)::mmap(0, size_, PROT_WRITE, MAP_SHARED, file_, 0);\n\n    assert (memory_ > 0);\n    return (memory_ != kInvalidAddr);\n}\n\nvoid OMmapFile::Truncate(std::size_t  size)\n{\n    if (size == size_)\n        return;\n\n    if (memory_ > 0)\n    {\n        //int ret = ::munmap(memory_, size_);\n        //assert (ret == 0);\n    }\n\n    size_ = size;\n    int ret = ::ftruncate(file_, size_);\n    assert (ret == 0);\n\n    if (offset_> size_)\n        offset_ = size_;\n\n    _MapWriteOnly();\n}\n\nbool OMmapFile::IsOpen() const\n{\n    return  file_ != kInvalidFile;\n}\n\n// consumer\nvoid OMmapFile::Write(const void* data, size_t len)\n{\n    _AssureSpace(len);\n\n    assert(memory_ > 0);\n    assert (offset_ + len <= size_);\n\n    ::memcpy(memory_ + offset_, data, len);\n    offset_ += len;\n    assert(offset_ <= size_);\n}\n\nvoid OMmapFile::_AssureSpace(size_t len)\n{\n    size_t newSize = size_;\n\n    while (offset_ + len > newSize)\n    {\n        if (newSize == 0)\n            newSize = kDefaultSize;\n        else\n            newSize *= 2;\n    }\n\n    _ExtendFileSize(newSize);\n}\n\n} // end namespace internal\n\n} // end namespace ananas\n\n"
  },
  {
    "path": "cluster/ananas/net/log/MmapFile.h",
    "content": "#ifndef BERT_MMAPFILE_H\n#define BERT_MMAPFILE_H\n\n#include <string>\n\nnamespace ananas\n{\nnamespace internal\n{\n\nclass OMmapFile\n{\npublic:\n    OMmapFile();\n   ~OMmapFile();\n\n    bool Open(const std::string& file, bool bAppend = true);\n    bool Open(const char* file, bool bAppend = true);\n    void Close();\n    bool Sync();\n\n    void Truncate(std::size_t size);\n\n    void Write(const void* data, std::size_t len);\n    template <typename T>\n    void Write(const T& t);\n    \n    std::size_t Offset() const { return offset_; }\n    bool IsOpen() const;\n\nprivate:\n    bool _MapWriteOnly();\n    void _ExtendFileSize(std::size_t size);\n    void _AssureSpace(std::size_t size);\n\n    int file_;\n    char* memory_;\n    std::size_t offset_;\n    std::size_t size_;\n    std::size_t syncPos_;\n};\n\n\ntemplate <typename T>\ninline void OMmapFile::Write(const T& t)\n{\n    this->Write(&t, sizeof t);\n}\n\n} // end namespace internal\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/net/log/README.md",
    "content": "# Multi-thread log\n# 多线程日志库\n\n## Requirements\n* C++11\n* Linux or MAC OS\n\n## High Performance\nO2优化,\n\n单线程写100万条日志600ms左右,多线程速度可提高到每100万日志300-400ms左右\n\n即每秒300w+\n\n但线程过多(`超过CPU个数`)可能会退化到单线程的水平.\n\n## High Reliability\n\n在进程关闭时候，IO线程退出前，同步等待生产者线程完成.\n\n并且此时将不能再创建新的logger对象，确保迅速收敛.\n\n生产者线程在日志关闭后写日志将得到提示并输出到屏幕.\n\n这一现象并不应该发生，只有在全局对象的析构函数调用日志可能会触发.\n\n由于C++不保证不同编译单元全局对象的构造和析构顺序，在析构函数中做过多的\n\n工作似乎也不是合理的。\n\n## Rationale\n\n每个生产者线程在格式化日志数据的时候是不需要锁的，因为访问的是thread-local数据.\n\n每个生产者线程有独立的输出buffer，通过thread id来索引.\n\n当输出buffer超过阈值，主动唤醒IO线程\n\n唯一的IO线程，将buffer输出到mmap的日志文件中.\n\n"
  },
  {
    "path": "cluster/ananas/util/Delegate.h",
    "content": "#ifndef BERT_DELEGATE_H\n#define BERT_DELEGATE_H\n\n#include <functional>\n#include <list>\n\n\nnamespace ananas\n{\n\ntemplate <typename T>\nclass Delegate;\n\ntemplate <typename... Args>\nclass Delegate<void (Args...)>\n{\npublic:\n    typedef Delegate<void (Args...)> Self;\n\n    Delegate() = default;\n\n    Delegate(const Self& ) = delete;\n    Self& operator= (const Self& ) = delete;\n\n    template <typename F>\n    Delegate(F&& f)\n    {\n        connect(std::forward<F>(f));\n    }\n\n    Delegate(Self&& other) :\n        funcs_(std::move(other.funcs_))\n    {\n    }\n    \n    template <typename F>\n    Self& operator+=(F&& f)\n    {\n        connect(std::forward<F>(f));\n        return *this;\n    }\n    \n    template <typename F>\n    Self& operator-=(F&& f)\n    {\n        disconnect(std::forward<F>(f));\n        return *this;\n    }\n    \n    template<typename... ARGS>\n    void operator()(ARGS&&... args)\n    {\n        call(std::forward<ARGS>(args)...);\n    }\n    \nprivate:\n    std::list<std::function<void (Args ...)> > funcs_;\n    \n    template <typename F>\n    void connect(F&& f)\n    {\n        funcs_.emplace_back(std::forward<F>(f));\n    }\n\n    template <typename F>\n    void disconnect(F&& f)\n    {\n        for (auto it(funcs_.begin()); it != funcs_.end(); ++ it)\n        {\n            const auto& target = it->template target<decltype(std::addressof(f))>();\n            if (target)\n            {\n                if (*target == &f)\n                {\n                    funcs_.erase(it);\n                    return;\n                }\n            }\n            else\n            {\n                const auto& target2 = it->template target<typename std::remove_reference<decltype(f)>::type>();\n\n                // the function object must implement operator ==\n                if (target2 && *target2 == f)\n                {\n                    funcs_.erase(it);\n                    return;\n                }\n            }\n        }\n    }\n\n    template <typename... ARGS>\n    void call(ARGS&&... args)\n    {\n        // But what if rvalue args?\n        for (const auto& f : funcs_)\n            f(std::forward<ARGS>(args)...);\n    }\n};\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/util/Scheduler.h",
    "content": "#ifndef BERT_SCHEDULER_H\n#define BERT_SCHEDULER_H\n\nnamespace ananas\n{\n\nclass Scheduler\n{\npublic:\n    virtual ~Scheduler() {}\n\n    /** \n     * The following functions need not to be thread-safe.\n     * It is nonsense that schedule callback and submit request are in different threads.\n     * Do it like this:\n     * e.g.\n     * \n     * // In this_loop thread.\n     * \n     * Future<Buffer> ft(ReadFileInSeparateThread(very_big_file));\n     * \n     * ft.Then(&this_loop, [](const Buffer& file_contents) {\n     *      // SUCCESS : process file_content;\n     * })\n     * .OnTimeout(std::chrono::seconds(10), [=very_big_file]() {\n     *      // FAILED OR TIMEOUT:\n     *      printf(\"Read file %s failed\\n\", very_big_file);\n     * },\n     * &this_loop);\n     */\n    virtual void ScheduleOnceAfter(std::chrono::milliseconds duration, std::function<void()> f) = 0;\n    virtual void ScheduleOnce(std::function<void()> f) = 0;\n};\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/ananas/util/Util.h",
    "content": "#ifndef BERT_UTIL_H\n#define BERT_UTIL_H\n\n// Ananas tool box\n\n#include <functional>\n#include <string>\n\nnamespace ananas\n{\n\nnamespace\n{\n\n// The defer class for C++11\nclass ExecuteOnScopeExit\n{\npublic:\n    ExecuteOnScopeExit() { }\n    \n    ExecuteOnScopeExit(ExecuteOnScopeExit&& e)\n    {\n        func_ = std::move(e.func_);\n    }\n    \n    ExecuteOnScopeExit(const ExecuteOnScopeExit& e) = delete;\n    void operator=(const ExecuteOnScopeExit& f) = delete;\n    \n    template <typename F, typename... Args>\n    ExecuteOnScopeExit(F&& f, Args&&... args)\n    {\n        auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);\n        func_ = [temp]() { (void)temp(); };\n    }\n    \n    ~ExecuteOnScopeExit() noexcept\n    {\n        if (func_)  func_();\n    }\n    \nprivate:\n    std::function<void ()> func_;\n};\n    \n} // end namespace\n\n#define _CONCAT(a, b) a##b\n#define _MAKE_DEFER_HELPER_(line)  ananas::ExecuteOnScopeExit _CONCAT(defer, line) = [&]()\n\n#undef ANANAS_DEFER\n#define ANANAS_DEFER _MAKE_DEFER_HELPER_(__LINE__)\n\ninline\nstd::vector<std::string> SplitString(const std::string& str, char seperator)\n{\n    std::vector<std::string> results;\n    \n    std::string::size_type start = 0;\n    std::string::size_type sep = str.find(seperator);\n    while (sep != std::string::npos)\n    {\n        if (start < sep)\n            results.emplace_back(str.substr(start, sep - start));\n        \n        start = sep + 1;\n        sep = str.find(seperator, start);\n    }\n    \n    if (start != str.size())\n        results.emplace_back(str.substr(start));\n    \n    return results;\n}\n\n} // end namespace ananas\n\n#endif\n\n"
  },
  {
    "path": "cluster/cluster_conn/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nSUBDIRS(zookeeper)\n"
  },
  {
    "path": "cluster/cluster_conn/ClusterConn.h",
    "content": "#ifndef BERT_CLUSTERCONN_H\n#define BERT_CLUSTERCONN_H\n\nnamespace qedis\n{\n\nclass ClusterConn\n{ \npublic:\n    virtual ~ClusterConn()\n    {\n    }\n\npublic:\n    virtual bool OnData(const char*& data, size_t len) = 0;\n    virtual void OnConnect() = 0;\n    virtual void OnDisconnect() = 0;\n};\n\n} // end namespace qedis\n\n#endif // endif BERT_CLUSTERCONN_H\n\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/CMakeLists.txt",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 2.6)\n\nINCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ananas)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/zk)\n\nAUX_SOURCE_DIRECTORY(. ZK_SRC)\n\nSET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)\nADD_LIBRARY(zk ${ZK_SRC})\nTARGET_LINK_LIBRARIES(zk; ananas; pthread)\nADD_DEPENDENCIES(zk; ananas)\n\nSET_TARGET_PROPERTIES(zk PROPERTIES LINKER_LANGUAGE CXX)\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/ZkResponse.h",
    "content": "#ifndef BERT_ZKRESPONSE_H\n#define BERT_ZKRESPONSE_H\n\n#include \"zookeeper.jute.h\"\n#include \"proto.h\"\n\nusing ZkResponse = std::shared_ptr<void>;\n\ntemplate <typename T>\ninline\nstd::shared_ptr<T> NewResponse()\n{\n    return std::make_shared<T>();\n}\n\ntemplate <typename T>\ninline std::shared_ptr<T> AnyCast(const ZkResponse& rsp)\n{\n    return std::static_pointer_cast<T>(rsp);\n}\n\nstruct CreateRsp\n{\n    std::string path;\n};\n\ninline\nCreateRsp Convert(const CreateResponse& rsp)\n{\n    CreateRsp crsp;\n    crsp.path = rsp.path;\n    return crsp;\n}\n\nstruct ChildrenRsp\n{\n    std::string parent;\n    std::vector<std::string> children;\n    struct Stat stat;\n};\n\ninline\nChildrenRsp Convert(const std::string& parent, const GetChildren2Response& children)\n{\n    ChildrenRsp rsp;\n    rsp.parent = parent;\n\n    for (int i = 0; i < children.children.count; ++ i)\n    {\n        rsp.children.push_back(children.children.data[i]);\n    }\n\n    rsp.stat = children.stat;\n    return rsp;\n}\n\nstruct DataRsp\n{\n    std::string path;\n    std::string data;\n    struct Stat stat;\n};\n\ninline\nDataRsp Convert(const std::string& path, const GetDataResponse& rsp)\n{\n    DataRsp drsp;\n    drsp.path = path;\n    drsp.data.assign(rsp.data.buff, rsp.data.len);\n    drsp.stat = rsp.stat;\n    return drsp;\n}\n\n#if 0\nunion ZkResponse\n{\n    prime_struct handshakeRsp;\n    long recvPing;\n    CreateResponse createRsp;\n\n    struct \n    {\n        struct buffer parent;\n        GetChildren2Response children;\n    } child2Rsp;\n\n    struct {\n        struct buffer path;\n        GetDataResponse data;\n    } dataRsp;\n};\n#endif\n\n#endif //BERT_ZKRESPONSE_H\n\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/ZookeeperContext.cc",
    "content": "#include <unistd.h>\n#include <sys/time.h>\n#include \"ZookeeperContext.h\"\n#include \"proto.h\"\n#include \"zookeeper.jute.h\"\n#include \"net/Connection.h\"\n#include \"net/Buffer.h\"\n#include \"util/Util.h\"\n\n#include <iostream>\n\n#include \"proto.h\"\n\nstatic void deserialize_prime_response(struct prime_struct *req, char* buffer)\n{\n     int offset = 0;\n     memcpy(&req->len, buffer + offset, sizeof(req->len));\n     offset = offset +  sizeof(req->len);\n\n     req->len = ntohl(req->len);\n     memcpy(&req->protocolVersion, buffer + offset, sizeof(req->protocolVersion));\n     offset = offset +  sizeof(req->protocolVersion);\n\n     req->protocolVersion = ntohl(req->protocolVersion);\n     memcpy(&req->timeOut, buffer + offset, sizeof(req->timeOut));\n     offset = offset +  sizeof(req->timeOut);\n\n     req->timeOut = ntohl(req->timeOut);\n     memcpy(&req->sessionId, buffer + offset, sizeof(req->sessionId));\n     offset = offset +  sizeof(req->sessionId);\n\n     req->sessionId = htonll(req->sessionId);\n     memcpy(&req->passwd_len, buffer + offset, sizeof(req->passwd_len));\n     offset = offset +  sizeof(req->passwd_len);\n\n     req->passwd_len = ntohl(req->passwd_len);\n     memcpy(req->passwd, buffer + offset, sizeof(req->passwd));\n}\n\nstatic void serialize_prime_connect(struct connect_req *req, char* buffer)\n{\n    int offset = 0;\n    req->protocolVersion = htonl(req->protocolVersion);\n    memcpy(buffer + offset, &req->protocolVersion, sizeof(req->protocolVersion));\n    offset = offset +  sizeof(req->protocolVersion);\n\n    req->lastZxidSeen = htonll(req->lastZxidSeen);\n    memcpy(buffer + offset, &req->lastZxidSeen, sizeof(req->lastZxidSeen));\n    offset = offset +  sizeof(req->lastZxidSeen);\n\n    req->timeOut = htonl(req->timeOut);\n    memcpy(buffer + offset, &req->timeOut, sizeof(req->timeOut));\n    offset = offset +  sizeof(req->timeOut);\n\n    req->sessionId = htonll(req->sessionId);\n    memcpy(buffer + offset, &req->sessionId, sizeof(req->sessionId));\n    offset = offset +  sizeof(req->sessionId);\n\n    req->passwd_len = htonl(req->passwd_len);\n    memcpy(buffer + offset, &req->passwd_len, sizeof(req->passwd_len));\n    offset = offset +  sizeof(req->passwd_len);\n\n    memcpy(buffer + offset, req->passwd, sizeof(req->passwd)); \n}\n\n\nconst int kTimeout = 10 * 1000; // ms\n\nnamespace qedis\n{\n    \nZookeeperContext::ZookeeperContext(ananas::Connection* c) :\n    conn_(c),\n    xid_(0),\n    state_(ZookeeperContext::State::kNone)\n{\n    sessionInfo_.passwd[0] = 0;\n\n    sessionFile_ = \"zk.session\" + c->Peer().ToString();\n}\n\nZookeeperContext::~ZookeeperContext()\n{\n}\n\nbool ZookeeperContext::ParseMessage(const char*& data, ananas::PacketLen_t len)\n{\n    switch (state_)\n    {\n    case State::kHandshaking:\n        {\n            if (len < HANDSHAKE_RSP_SIZE)\n                return true;\n\n            auto zkrsp = NewResponse<prime_struct>();\n            deserialize_prime_response(zkrsp.get(), const_cast<char* >(data));\n            data += sizeof(int) + zkrsp->len;\n            std::cout << \"skip kHandshaking \" << zkrsp->len << std::endl;\n\n            pendingReq_.front().promise.SetValue(AnyCast<void>(zkrsp));\n            pendingReq_.pop();\n        }\n        break;\n\n    case State::kConnected:\n        {\n            assert (len >= 4);\n            int thisLen = *(int*)data;\n            thisLen = ntohl(thisLen);\n            if (sizeof thisLen + thisLen > len)\n            {\n                std::cout << \"thisLen is \" << thisLen << \", only len \" << len << std::endl;\n                return true; // not enough\n            }\n                \n            std::cout << \"thisLen is \" << thisLen << \", and len \" << len << std::endl;\n            \n            struct ReplyHeader hdr;\n            struct iarchive *ia = create_buffer_iarchive(const_cast<char* >(data) + 4, thisLen); \n            deserialize_ReplyHeader(ia, \"hdr\", &hdr);\n\n            if (hdr.zxid > 0)\n                sessionInfo_.lastZxidSeen = hdr.zxid;\n\n            if (!_ProcessResponse(hdr, ia))\n                return false;\n\n            data += thisLen + sizeof thisLen;\n            return true;\n        }\n        break;\n    default:\n        assert (false);\n        break;\n    }\n\n    return true;\n}\n            \nbool ZookeeperContext::_ProcessResponse(const ReplyHeader& hdr, iarchive* ia)\n{\n    if (hdr.err != 0)\n    {\n        std::cout << \"TODO hdr.err \" << hdr.err << std::endl;\n        //return false;\n    }\n\n    if (hdr.xid == WATCHER_EVENT_XID)\n    {\n        std::cout << \"TODO WATCHER_EVENT_XID\\n\";\n        return false;\n        //return _ProcessWatchEvent(hdr, ia);\n    }\n\n    if (pendingReq_.empty())\n    {\n        std::cerr << \"Can not find request \" << hdr.xid << std::endl;\n        return false;\n    }\n\n    auto& req = pendingReq_.front();\n    ANANAS_DEFER\n    {\n        if (hdr.err)\n            std::cout << \"Error \" << hdr.err << \" with \" << req.path << std::endl;\n\n        pendingReq_.pop();\n    };\n\n    if (req.xid != hdr.xid)\n    {\n        std::cerr << \"Req xid \" << req.xid << \", recv wrong order response \" << hdr.xid << std::endl;\n        return false;\n    }\n\n    switch (req.type)\n    {\n    case ZOO_PING_OP:\n        {\n            timeval now;\n            gettimeofday(&now, nullptr);\n\n            auto rsp = NewResponse<long>();\n            *rsp = now.tv_sec * 1000;\n            *rsp += now.tv_usec  / 1000;\n\n            req.promise.SetValue(AnyCast<void>(rsp));\n        }\n        break;\n\n    case ZOO_CREATE_OP:\n        {\n            CreateResponse crsp;\n            if (deserialize_CreateResponse(ia, \"rsp\", &crsp) != 0)\n            {\n                std::cout << \"deserialize_CreateResponse failed\\n\";\n                return false;\n            }\n\n            ANANAS_DEFER\n            {\n                deallocate_CreateResponse(&crsp);\n            };\n\n            auto rsp = NewResponse<CreateRsp>();\n            *rsp = Convert(crsp);\n\n            req.promise.SetValue(AnyCast<void>(rsp));\n        }\n        break;\n\n    case ZOO_GETCHILDREN2_OP:\n        {\n            GetChildren2Response grsp;\n            if (deserialize_GetChildren2Response(ia, \"rsp\", &grsp) != 0)\n            {\n                std::cout << \"deserialize_GetChildren2Response failed\\n\";\n                return false;\n            }\n\n            ANANAS_DEFER\n            {\n                deallocate_GetChildren2Response(&grsp);\n            };\n\n            auto rsp = NewResponse<ChildrenRsp>();\n            *rsp = Convert(req.path, grsp);\n            req.promise.SetValue(AnyCast<void>(rsp));\n        }\n        break;\n\n    case ZOO_GETDATA_OP:\n        {\n            GetDataResponse drsp;\n            if (deserialize_GetDataResponse(ia, \"rsp\", &drsp) != 0)\n            {\n                std::cout << \"deserialize_GetDataResponse failed\\n\";\n                return false;\n            }\n\n            ANANAS_DEFER\n            {\n                deallocate_GetDataResponse(&drsp);\n            };\n\n            auto rsp = NewResponse<DataRsp>();\n            *rsp = Convert(req.path, drsp);\n\n            req.promise.SetValue(AnyCast<void>(rsp));\n        }\n        break;\n\n    default:\n        std::cout << \"TODO req type \" << req.type << std::endl;\n        break;\n    }\n\n    return true;\n}\n    \nvoid ZookeeperContext::ProcessPing(long lastPing, ZkResponse now)\n{\n    auto rsp = AnyCast<long>(now);\n    int millseconds = *rsp - lastPing;\n    if (millseconds > 1)\n        std::cout << \"ProcessPing used millseconds:\" << millseconds << std::endl;\n}\n\nbool ZookeeperContext::ProcessHandshake(const ZkResponse& rsp)\n{\n    auto hrsp = AnyCast<prime_struct>(rsp);\n    if (sessionInfo_.sessionId && sessionInfo_.sessionId != hrsp->sessionId)\n    {\n        std::cout << \"expired old session \" << sessionInfo_.sessionId << \", new session \" << hrsp->sessionId << std::endl;\n        return false;\n    }\n\n    resumed_ = (sessionInfo_.sessionId == hrsp->sessionId);\n    if (resumed_)\n        std::cout << \"resume session Id \" << hrsp->sessionId << std::endl;\n    else\n        std::cout << \"new session Id \" << hrsp->sessionId << std::endl;\n\n    // record this sessionInfo;\n    sessionInfo_.sessionId = hrsp->sessionId;\n    memcpy(sessionInfo_.passwd, hrsp->passwd, hrsp->passwd_len);\n\n    std::unique_ptr<FILE, decltype(fclose)*> _(fopen(sessionFile_.data(), \"wb\"), fclose);\n    FILE* fp = _.get();\n    assert (fp);\n    fwrite(&sessionInfo_, sizeof sessionInfo_, 1, fp);\n\n    assert (state_ == State::kHandshaking);\n    state_  = State::kConnected;\n\n    std::cout << \"recv ProcessHandshake sid \" << hrsp->sessionId << std::endl;\n    return true;\n}\n\nananas::Future<ZkResponse> ZookeeperContext::DoHandshake()\n{\n    {\n        auto del = [this](FILE* fp) {\n            ::fclose(fp);\n            ::unlink(sessionFile_.c_str());\n        };\n\n        std::unique_ptr<FILE, decltype(del)> _(fopen(sessionFile_.data(), \"rb\"), del);\n        FILE* const fp = _.get();\n\n        if (fp)\n            fread(&sessionInfo_, sizeof sessionInfo_, 1, fp);\n    }\n\n    struct connect_req req; \n    memset(&req, 0, sizeof req);\n    req.timeOut = kTimeout;\n    req.sessionId = sessionInfo_.sessionId;\n    req.lastZxidSeen = sessionInfo_.lastZxidSeen;\n    req.passwd_len = sizeof(req.passwd);\n    memcpy(req.passwd, sessionInfo_.passwd, req.passwd_len);\n                    \n    char buffer[HANDSHAKE_REQ_SIZE];\n    int len = HANDSHAKE_REQ_SIZE;\n    len = htonl(len);\n    serialize_prime_connect(&req, buffer);\n    \n    struct ananas::SliceVector v;\n    v.PushBack(&len, sizeof len);\n    v.PushBack(buffer, HANDSHAKE_REQ_SIZE);\n    conn_->SendPacket(v);\n\n    state_ = State::kHandshaking;\n\n    return _PendingRequest(0, 0);\n}\n\nananas::Future<ZkResponse> ZookeeperContext::Ping()\n{\n    struct oarchive *oa = create_buffer_oarchive();\n    struct RequestHeader h = { STRUCT_INITIALIZER(xid, PING_XID), STRUCT_INITIALIZER (type , ZOO_PING_OP) };\n\n    serialize_RequestHeader(oa, \"header\", &h);\n\n    _SendPacket(oa);\n    return _PendingRequest(ZOO_PING_OP, PING_XID);\n}\n\nstatic struct ACL _OPEN_ACL_UNSAFE_ACL[] = {{0x1f, {\"world\", \"anyone\"}}};\nstruct ACL_vector ZOO_OPEN_ACL_UNSAFE = { 1, _OPEN_ACL_UNSAFE_ACL};\n\nananas::Future<ZkResponse> ZookeeperContext::CreateNode(bool empher,\n                                                        bool seq,\n                                                        const std::string* data,\n                                                        const std::string* pathPrefix)\n{\n    struct oarchive *oa = create_buffer_oarchive();\n    struct RequestHeader h = { STRUCT_INITIALIZER (xid , _GetXid()), STRUCT_INITIALIZER (type ,ZOO_CREATE_OP) };\n    int rc = serialize_RequestHeader(oa, \"header\", &h);\n    if (rc < 0)\n    {\n        close_buffer_oarchive(&oa, 1);\n        return ananas::MakeExceptionFuture<ZkResponse>(std::runtime_error(\"serialize_RequestHeader failed when try to create node\"));\n    }\n\n    struct CreateRequest req;\n    req.path = const_cast<char* >(pathPrefix->data());\n    req.data.buff = const_cast<char* >(data->data());\n    req.data.len = static_cast<int32_t>(data->size());\n    req.acl = ZOO_OPEN_ACL_UNSAFE;\n    req.flags = 0;\n    if (empher) req.flags |= ZOO_SEQUENCE;\n    if (empher) req.flags |= ZOO_EPHEMERAL;\n\n    rc = rc < 0 ? rc : serialize_CreateRequest(oa, \"req\", &req);\n\n    _SendPacket(oa);\n    return _PendingRequest(h.type, h.xid);\n}\n\nvoid ZookeeperContext::ProcessCreateNode(const ZkResponse& rsp)\n{\n    auto crsp = AnyCast<CreateRsp>(rsp);\n    std::cout << \"Create Node \" << crsp->path << std::endl;\n}\n\nananas::Future<ZkResponse> ZookeeperContext::GetChildren2(const std::string& parent, bool watch)\n{\n    struct oarchive* oa = create_buffer_oarchive();\n\n    struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER(type ,ZOO_GETCHILDREN2_OP)};\n    struct GetChildren2Request req;\n    req.path = const_cast<char* >(parent.data());\n    req.watch = watch ? 1 : 0;\n    \n    int rc = serialize_RequestHeader(oa, \"header\", &h); \n    rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, \"req\", &req);\n\n    _SendPacket(oa);\n    return _PendingRequest(h.type, h.xid, &parent);\n}\n\nvoid ZookeeperContext::ProcessGetChildren2(const ZkResponse& rsp)\n{\n    auto grsp = AnyCast<ChildrenRsp>(rsp);\n    std::cout << \"GetChildren2 for \" << grsp->parent << std::endl;\n\n    for (const auto& c : grsp->children)\n    {\n        std::cout << \"child: \" << c << std::endl;\n    }\n}\n\nananas::Future<ZkResponse> ZookeeperContext::GetData(const std::string& node, bool watch)\n{\n    struct oarchive* oa = create_buffer_oarchive();\n\n    struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER(type ,ZOO_GETDATA_OP)};\n    struct GetDataRequest req;\n    req.path = const_cast<char* >(node.data());\n    req.watch = watch ? 1 : 0;\n    \n    int rc = serialize_RequestHeader(oa, \"header\", &h); \n    rc = rc < 0 ? rc : serialize_GetDataRequest(oa, \"req\", &req);\n\n    _SendPacket(oa);\n    return _PendingRequest(h.type, h.xid, &node);\n}\n\nvoid ZookeeperContext::ProcessGetData(const ZkResponse& rsp)\n{\n    auto drsp = AnyCast<DataRsp>(rsp);\n    std::cout << \"GetData \" << drsp->path << \" : \" << drsp->data << std::endl;\n}\n\n\nint ZookeeperContext::_GetXid() const\n{\n    return ++ xid_; \n}\n    \nananas::Future<ZkResponse> ZookeeperContext::_PendingRequest(int type, int xid, const std::string* path)\n{\n    struct Request req;\n    req.type = type;\n    req.xid = xid;\n    if (path) req.path = *path;\n\n    auto fut = req.promise.GetFuture();\n    pendingReq_.push(std::move(req));\n\n    return fut;\n}\n\nvoid ZookeeperContext::_SendPacket(struct oarchive* oa)\n{\n    int totalLen = htonl(get_buffer_len(oa));\n\n    struct ananas::SliceVector v;\n    v.PushBack(&totalLen, sizeof totalLen);\n    v.PushBack(get_buffer(oa), get_buffer_len(oa));\n\n    conn_->SendPacket(v);\n    close_buffer_oarchive(&oa, 1);\n}\n\n} // end namespace qedis\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/ZookeeperContext.h",
    "content": "#ifndef BERT_ZOOKEEPERCONTEXT_H\n#define BERT_ZOOKEEPERCONTEXT_H\n\n#include <queue>\n#include <string>\n#include \"future/Future.h\"\n#include \"net/Typedefs.h\"\n#include \"ZkResponse.h\"\n\nstruct oarchive;\nstruct iarchive;\n\nnamespace ananas\n{\nclass Connection;\n}\n\nnamespace qedis\n{\n\nclass ZookeeperContext final\n{\npublic:\n    explicit\n    ZookeeperContext(ananas::Connection* c);\n    ~ZookeeperContext();\n\n    bool ParseMessage(const char*& data, ananas::PacketLen_t len);\n\n    bool IsResumed() const { return resumed_; }\n\n    // requests\n    ananas::Future<ZkResponse> DoHandshake();\n    bool ProcessHandshake(const ZkResponse& rsp);\n\n    // Ping\n    ananas::Future<ZkResponse> Ping();\n    void ProcessPing(long lastPing, ZkResponse now); // Unit: millseconds\n\n    // Create node\n    ananas::Future<ZkResponse> CreateNode(bool empher, bool seq,\n                                          const std::string* data = nullptr,\n                                          const std::string* pathPrefix = nullptr);\n    void ProcessCreateNode(const ZkResponse& rsp);\n\n    // Get children2\n    ananas::Future<ZkResponse> GetChildren2(const std::string& parent, bool watch = false);\n    void ProcessGetChildren2(const ZkResponse& rsp);\n\n    // Get data \n    ananas::Future<ZkResponse> GetData(const std::string& node, bool watch = false);\n    void ProcessGetData(const ZkResponse& rsp);\n\nprivate:\n\n    void _SendPacket(struct oarchive* oa);\n\n    ananas::Connection* const conn_;\n\n    int _GetXid() const;\n    mutable int xid_;\n    \n    int lastZxid_;\n\n    struct Request {\n        ananas::Promise<ZkResponse> promise;\n        int type;\n        int xid;\n        std::string path;\n        \n        Request() : type(-1), xid(-1) { }\n\n        Request(const Request& ) = delete;\n        void operator= (const Request & ) = delete;\n\n        Request(Request&& r) {\n            _Move(std::move(r));\n        }\n\n        Request& operator= (Request&& r) {\n            return _Move(std::move(r));\n        }\n    private:\n        Request& _Move(Request&& r) {\n            if (&r != this) {\n                promise = std::move(r.promise);\n                type = r.type;\n                xid = r.xid;\n                path = std::move(r.path);\n                r.type = r.xid = -1;\n            }\n            return *this;\n        }\n    };\n\n    ananas::Future<ZkResponse> _PendingRequest(int type, int xid, const std::string* path = nullptr);\n\n    std::queue<Request> pendingReq_;\n\n    enum class State\n    {\n        kNone,\n        kHandshaking,\n        kConnected,\n    } state_;\n\n    bool _ProcessResponse(const ReplyHeader& hdr, iarchive* ia);\n\n#pragma pack(1)\n    struct SessionInfo\n    {\n        int64_t sessionId{0};\n        char passwd[16];\n        int64_t lastZxidSeen {0}; // zk_cli doesn't persist this field\n    } sessionInfo_;\n#pragma pack()\n    std::string sessionFile_;\n    bool resumed_ {false};\n};\n\n} // end namespace qedis\n\n#endif //endif BERT_ZOOKEEPERCONN_H\n\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/proto.h",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#ifndef PROTO_H_\n#define PROTO_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\nconst int ZOO_EPHEMERAL = 1 << 0;\nconst int ZOO_SEQUENCE = 1 << 1;\n\n#define ZOO_NOTIFY_OP 0\n#define ZOO_CREATE_OP 1\n#define ZOO_DELETE_OP 2\n#define ZOO_EXISTS_OP 3\n#define ZOO_GETDATA_OP 4\n#define ZOO_SETDATA_OP 5\n#define ZOO_GETACL_OP 6\n#define ZOO_SETACL_OP 7\n#define ZOO_GETCHILDREN_OP 8\n#define ZOO_SYNC_OP 9\n#define ZOO_PING_OP 11\n#define ZOO_GETCHILDREN2_OP 12\n#define ZOO_CHECK_OP 13\n#define ZOO_MULTI_OP 14\n#define ZOO_CLOSE_OP -11\n#define ZOO_SETAUTH_OP 100\n#define ZOO_SETWATCHES_OP 101\n\n\n/* the size of connect request */\n#define HANDSHAKE_REQ_SIZE 44\n/* connect request */\nstruct connect_req {\n    int32_t protocolVersion;\n    int64_t lastZxidSeen;\n    int32_t timeOut;\n    int64_t sessionId;\n    int32_t passwd_len;\n    char passwd[16];\n};\n\n#define HANDSHAKE_RSP_SIZE 40\n/* the connect response */\nstruct prime_struct {\n    int32_t len;\n    int32_t protocolVersion;\n    int32_t timeOut;\n    int64_t sessionId;\n    int32_t passwd_len;\n    char passwd[16];\n}; \n\nenum ZOO_ERRORS {\n  ZOK = 0, /*!< Everything is OK */\n\n  /** System and server-side errors.\n   * This is never thrown by the server, it shouldn't be used other than\n   * to indicate a range. Specifically error codes greater than this\n   * value, but lesser than {@link #ZAPIERROR}, are system errors. */\n  ZSYSTEMERROR = -1,\n  ZRUNTIMEINCONSISTENCY = -2, /*!< A runtime inconsistency was found */\n  ZDATAINCONSISTENCY = -3, /*!< A data inconsistency was found */\n  ZCONNECTIONLOSS = -4, /*!< Connection to the server has been lost */\n  ZMARSHALLINGERROR = -5, /*!< Error while marshalling or unmarshalling data */\n  ZUNIMPLEMENTED = -6, /*!< Operation is unimplemented */\n  ZOPERATIONTIMEOUT = -7, /*!< Operation timeout */\n  ZBADARGUMENTS = -8, /*!< Invalid arguments */\n  ZINVALIDSTATE = -9, /*!< Invliad zhandle state */\n\n  /** API errors.\n   * This is never thrown by the server, it shouldn't be used other than\n   * to indicate a range. Specifically error codes greater than this\n   * value are API errors (while values less than this indicate a \n   * {@link #ZSYSTEMERROR}).\n   */\n  ZAPIERROR = -100,\n  ZNONODE = -101, /*!< Node does not exist */\n  ZNOAUTH = -102, /*!< Not authenticated */\n  ZBADVERSION = -103, /*!< Version conflict */\n  ZNOCHILDRENFOREPHEMERALS = -108, /*!< Ephemeral nodes may not have children */\n  ZNODEEXISTS = -110, /*!< The node already exists */\n  ZNOTEMPTY = -111, /*!< The node has children */\n  ZSESSIONEXPIRED = -112, /*!< The session has been expired by the server */\n  ZINVALIDCALLBACK = -113, /*!< Invalid callback specified */\n  ZINVALIDACL = -114, /*!< Invalid ACL specified */\n  ZAUTHFAILED = -115, /*!< Client authentication failed */\n  ZCLOSING = -116, /*!< ZooKeeper is closing */\n  ZNOTHING = -117, /*!< (not error) no server responses to process */\n  ZSESSIONMOVED = -118 /*!<session moved to another server, so operation is ignored */ \n};\n\nstatic const int WATCHER_EVENT_XID = -1;\nstatic const int PING_XID = -2;\nstatic const int AUTH_XID = -4;\nstatic const int SET_WATCHES_XID = -8;\n\n/* zookeeper event type constants */\n#define CREATED_EVENT_DEF 1\n#define DELETED_EVENT_DEF 2\n#define CHANGED_EVENT_DEF 3\n#define CHILD_EVENT_DEF 4\n#define SESSION_EVENT_DEF -1\n#define NOTWATCHING_EVENT_DEF -2\n\n\n/* zookeeper state constants */\n#define EXPIRED_SESSION_STATE_DEF -112\n#define AUTH_FAILED_STATE_DEF -113\n#define CONNECTING_STATE_DEF 1\n#define ASSOCIATING_STATE_DEF 2\n#define CONNECTED_STATE_DEF 3\n#define NOTCONNECTED_STATE_DEF 999\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*PROTO_H_*/\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/recordio.c",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#include \"recordio.h\"\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <netinet/in.h>\n\nvoid deallocate_String(char **s)\n{\n    if (*s)\n        free(*s);\n    *s = 0;\n}\n\nvoid deallocate_Buffer(struct buffer *b)\n{\n    if (b->buff)\n        free(b->buff);\n    b->buff = 0;\n}\n\nstruct buff_struct {\n    int32_t len;\n    int32_t off;\n    char *buffer;\n};\n\nstatic int resize_buffer(struct buff_struct *s, int newlen)\n{\n    char *buffer= NULL;\n    while (s->len < newlen) {\n        s->len *= 2;\n    }\n    buffer = (char*)realloc(s->buffer, s->len);\n    if (!buffer) {\n        s->buffer = 0;\n        return -ENOMEM;\n    }\n    s->buffer = buffer;\n    return 0;\n}\n\nint oa_start_record(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_end_record(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d)\n{\n    struct buff_struct *priv = oa->priv;\n    int32_t i = htonl(*d);\n    if ((priv->len - priv->off) < sizeof(i)) {\n        int rc = resize_buffer(priv, priv->len + sizeof(i));\n        if (rc < 0) return rc;\n    }\n    memcpy(priv->buffer+priv->off, &i, sizeof(i));\n    priv->off+=sizeof(i);\n    return 0;\n}\n#ifndef __APPLE__\nint64_t htonll(int64_t v)\n{\n    int i = 0;\n    char *s = (char *)&v;\n    if (htonl(1) == 1) {\n        return v;\n    }\n    for (i = 0; i < 4; i++) {\n        int tmp = s[i];\n        s[i] = s[8-i-1];\n        s[8-i-1] = tmp;\n    }\n\n    return v;\n}\n#endif\n\nint oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d)\n{\n    const int64_t i = htonll(*d);\n    struct buff_struct *priv = oa->priv;\n    if ((priv->len - priv->off) < sizeof(i)) {\n        int rc = resize_buffer(priv, priv->len + sizeof(i));\n        if (rc < 0) return rc;\n    }\n    memcpy(priv->buffer+priv->off, &i, sizeof(i));\n    priv->off+=sizeof(i);\n    return 0;\n}\nint oa_start_vector(struct oarchive *oa, const char *tag, const int32_t *count)\n{\n    return oa_serialize_int(oa, tag, count);\n}\nint oa_end_vector(struct oarchive *oa, const char *tag)\n{\n    return 0;\n}\nint oa_serialize_bool(struct oarchive *oa, const char *name, const int32_t *i)\n{\n    //return oa_serialize_int(oa, name, i);\n    struct buff_struct *priv = oa->priv;\n    if ((priv->len - priv->off) < 1) {\n        int rc = resize_buffer(priv, priv->len + 1);\n        if (rc < 0)\n            return rc;\n    }\n    priv->buffer[priv->off] = (*i == 0 ? '\\0' : '\\1');\n    priv->off++;\n    return 0;\n}\nstatic const int32_t negone = -1;\nint oa_serialize_buffer(struct oarchive *oa, const char *name,\n        const struct buffer *b)\n{\n    struct buff_struct *priv = oa->priv;\n    int rc;\n    if (!b) {\n        return oa_serialize_int(oa, \"len\", &negone);\n    }\n    rc = oa_serialize_int(oa, \"len\", &b->len);\n    if (rc < 0)\n        return rc;\n    // this means a buffer of NUll \n    // with size of -1. This is \n    // waht we use in java serialization for NULL\n    if (b->len == -1) {\n      return rc;\n    }\n    if ((priv->len - priv->off) < b->len) {\n        rc = resize_buffer(priv, priv->len + b->len);\n        if (rc < 0)\n            return rc;\n    }\n    memcpy(priv->buffer+priv->off, b->buff, b->len);\n    priv->off += b->len;\n    return 0;\n}\nint oa_serialize_string(struct oarchive *oa, const char *name, char **s)\n{\n    struct buff_struct *priv = oa->priv;\n    int32_t len;\n    int rc;\n    if (!*s) {\n        oa_serialize_int(oa, \"len\", &negone);\n        return 0;\n    }\n    len = strlen(*s);\n    rc = oa_serialize_int(oa, \"len\", &len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < len) {\n        rc = resize_buffer(priv, priv->len + len);\n        if (rc < 0)\n            return rc;\n    }\n    memcpy(priv->buffer+priv->off, *s, len);\n    priv->off += len;\n    return 0;\n}\nint ia_start_record(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_end_record(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_deserialize_int(struct iarchive *ia, const char *tag, int32_t *count)\n{\n    struct buff_struct *priv = ia->priv;\n    if ((priv->len - priv->off) < sizeof(*count)) {\n        return -E2BIG;\n    }\n    memcpy(count, priv->buffer+priv->off, sizeof(*count));\n    priv->off+=sizeof(*count);\n    *count = ntohl(*count);\n    return 0;\n}\n\nint ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count)\n{\n    struct buff_struct *priv = ia->priv;\n    int64_t v = 0;\n    if ((priv->len - priv->off) < sizeof(*count)) {\n        return -E2BIG;\n    }\n    memcpy(count, priv->buffer+priv->off, sizeof(*count));\n    priv->off+=sizeof(*count);\n    v = htonll(*count); // htonll and  ntohll do the same\n    *count = v;\n    return 0;\n}\nint ia_start_vector(struct iarchive *ia, const char *tag, int32_t *count)\n{\n    return ia_deserialize_int(ia, tag, count);\n}\nint ia_end_vector(struct iarchive *ia, const char *tag)\n{\n    return 0;\n}\nint ia_deserialize_bool(struct iarchive *ia, const char *name, int32_t *v)\n{\n    struct buff_struct *priv = ia->priv;\n    //fprintf(stderr, \"Deserializing bool %d\\n\", priv->off);\n    //return ia_deserialize_int(ia, name, v);\n    if ((priv->len - priv->off) < 1) {\n        return -E2BIG;\n    }\n    *v = priv->buffer[priv->off];\n    priv->off+=1;\n    //fprintf(stderr, \"Deserializing bool end %d\\n\", priv->off);\n    return 0;\n}\nint ia_deserialize_buffer(struct iarchive *ia, const char *name,\n        struct buffer *b)\n{\n    struct buff_struct *priv = ia->priv;\n    int rc = ia_deserialize_int(ia, \"len\", &b->len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < b->len) {\n        return -E2BIG;\n    }\n    // set the buffer to null\n    if (b->len == -1) {\n       b->buff = NULL;\n       return rc;\n    }\n    b->buff = malloc(b->len);\n    if (!b->buff) {\n        return -ENOMEM;\n    }\n    memcpy(b->buff, priv->buffer+priv->off, b->len);\n    priv->off += b->len;\n    return 0;\n}\nint ia_deserialize_string(struct iarchive *ia, const char *name, char **s)\n{\n    struct buff_struct *priv = ia->priv;\n    int32_t len;\n    int rc = ia_deserialize_int(ia, \"len\", &len);\n    if (rc < 0)\n        return rc;\n    if ((priv->len - priv->off) < len) {\n        return -E2BIG;\n    }\n    if (len < 0) {\n        return -EINVAL;\n    }\n    *s = malloc(len+1);\n    if (!*s) {\n        return -ENOMEM;\n    }\n    memcpy(*s, priv->buffer+priv->off, len);\n    (*s)[len] = '\\0';\n    priv->off += len;\n    return 0;\n}\n\nstatic struct iarchive ia_default = { STRUCT_INITIALIZER (start_record ,ia_start_record),\n        STRUCT_INITIALIZER (end_record ,ia_end_record), STRUCT_INITIALIZER (start_vector , ia_start_vector),\n        STRUCT_INITIALIZER (end_vector ,ia_end_vector), STRUCT_INITIALIZER (deserialize_Bool , ia_deserialize_bool),\n        STRUCT_INITIALIZER (deserialize_Int ,ia_deserialize_int),\n        STRUCT_INITIALIZER (deserialize_Long , ia_deserialize_long) ,\n        STRUCT_INITIALIZER (deserialize_Buffer, ia_deserialize_buffer),\n        STRUCT_INITIALIZER (deserialize_String, ia_deserialize_string)   };\n\nstatic struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_start_record),\n        STRUCT_INITIALIZER (end_record , oa_end_record), STRUCT_INITIALIZER (start_vector , oa_start_vector),\n        STRUCT_INITIALIZER (end_vector , oa_end_vector), STRUCT_INITIALIZER (serialize_Bool , oa_serialize_bool),\n        STRUCT_INITIALIZER (serialize_Int , oa_serialize_int),\n        STRUCT_INITIALIZER (serialize_Long , oa_serialize_long) ,\n        STRUCT_INITIALIZER (serialize_Buffer , oa_serialize_buffer),\n        STRUCT_INITIALIZER (serialize_String , oa_serialize_string) };\n\nstruct iarchive *create_buffer_iarchive(char *buffer, int len)\n{\n    struct iarchive *ia = malloc(sizeof(*ia));\n    struct buff_struct *buff = malloc(sizeof(struct buff_struct));\n    if (!ia) return 0;\n    if (!buff) {\n        free(ia);\n        return 0;\n    }\n    *ia = ia_default;\n    buff->off = 0;\n    buff->buffer = buffer;\n    buff->len = len;\n    ia->priv = buff;\n    return ia;\n}\n\nstruct oarchive *create_buffer_oarchive()\n{\n    struct oarchive *oa = malloc(sizeof(*oa));\n    struct buff_struct *buff = malloc(sizeof(struct buff_struct));\n    if (!oa) return 0;\n    if (!buff) {\n        free(oa);\n        return 0;\n    }\n    *oa = oa_default;\n    buff->off = 0;\n    buff->buffer = malloc(128);\n    buff->len = 128;\n    oa->priv = buff;\n    return oa;\n}\n\nvoid close_buffer_iarchive(struct iarchive **ia)\n{\n    free((*ia)->priv);\n    free(*ia);\n    *ia = 0;\n}\n\nvoid close_buffer_oarchive(struct oarchive **oa, int free_buffer)\n{\n    if (free_buffer) {\n        struct buff_struct *buff = (struct buff_struct *)(*oa)->priv;\n        if (buff->buffer) {\n            free(buff->buffer);\n        }\n    }\n    free((*oa)->priv);\n    free(*oa);\n    *oa = 0;\n}\n\nchar *get_buffer(struct oarchive *oa)\n{\n    struct buff_struct *buff = oa->priv;\n    return buff->buffer;\n}\nint get_buffer_len(struct oarchive *oa)\n{\n    struct buff_struct *buff = oa->priv;\n    return buff->off;\n}\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/recordio.h",
    "content": "/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  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#ifndef __RECORDIO_H__\n#define __RECORDIO_H__\n\n#include <sys/types.h>\n#define STRUCT_INITIALIZER(l,r) .l = r\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct buffer {\n    int32_t len;\n    char *buff;\n};\n\nvoid deallocate_String(char **s);\nvoid deallocate_Buffer(struct buffer *b);\nvoid deallocate_vector(void *d);\nstruct iarchive {\n    int (*start_record)(struct iarchive *ia, const char *tag);\n    int (*end_record)(struct iarchive *ia, const char *tag);\n    int (*start_vector)(struct iarchive *ia, const char *tag, int32_t *count);\n    int (*end_vector)(struct iarchive *ia, const char *tag);\n    int (*deserialize_Bool)(struct iarchive *ia, const char *name, int32_t *);\n    int (*deserialize_Int)(struct iarchive *ia, const char *name, int32_t *);\n    int (*deserialize_Long)(struct iarchive *ia, const char *name, int64_t *);\n    int (*deserialize_Buffer)(struct iarchive *ia, const char *name,\n            struct buffer *);\n    int (*deserialize_String)(struct iarchive *ia, const char *name, char **);\n    void *priv;\n};\nstruct oarchive {\n    int (*start_record)(struct oarchive *oa, const char *tag);\n    int (*end_record)(struct oarchive *oa, const char *tag);\n    int (*start_vector)(struct oarchive *oa, const char *tag, const int32_t *count);\n    int (*end_vector)(struct oarchive *oa, const char *tag);\n    int (*serialize_Bool)(struct oarchive *oa, const char *name, const int32_t *);\n    int (*serialize_Int)(struct oarchive *oa, const char *name, const int32_t *);\n    int (*serialize_Long)(struct oarchive *oa, const char *name,\n            const int64_t *);\n    int (*serialize_Buffer)(struct oarchive *oa, const char *name,\n            const struct buffer *);\n    int (*serialize_String)(struct oarchive *oa, const char *name, char **);\n    void *priv;\n};\n\nstruct oarchive *create_buffer_oarchive(void);\nvoid close_buffer_oarchive(struct oarchive **oa, int free_buffer);\nstruct iarchive *create_buffer_iarchive(char *buffer, int len);\nvoid close_buffer_iarchive(struct iarchive **ia);\nchar *get_buffer(struct oarchive *);\nint get_buffer_len(struct oarchive *);\n\n#if defined(__APPLE__)\n#else\nint64_t htonll(int64_t v);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/zookeeper.jute.c",
    "content": "/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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#include <stdlib.h>\n#include \"zookeeper.jute.h\"\n\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_String(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_String(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Id(struct Id*v){\n    deallocate_String(&v->scheme);\n    deallocate_String(&v->id);\n}\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"perms\", &v->perms);\n    rc = rc ? rc : serialize_Id(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"perms\", &v->perms);\n    rc = rc ? rc : deserialize_Id(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ACL(struct ACL*v){\n    deallocate_Id(&v->id);\n}\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Int(out, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : out->serialize_Int(out, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Int(in, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : in->deserialize_Int(in, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Stat(struct Stat*v){\n}\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_StatPersisted(struct StatPersisted*v){\n}\nint serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_StatPersistedV1(struct StatPersistedV1*v){\n}\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Long(out, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Long(in, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectRequest(struct ConnectRequest*v){\n    deallocate_Buffer(&v->passwd);\n}\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectResponse(struct ConnectResponse*v){\n    deallocate_Buffer(&v->passwd);\n}\nint allocate_String_vector(struct String_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_String_vector(struct String_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_String(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : out->serialize_String(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : in->deserialize_String(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : serialize_String_vector(out, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : deserialize_String_vector(in, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetWatches(struct SetWatches*v){\n    deallocate_String_vector(&v->dataWatches);\n    deallocate_String_vector(&v->existWatches);\n    deallocate_String_vector(&v->childWatches);\n}\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_RequestHeader(struct RequestHeader*v){\n}\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Bool(out, \"done\", &v->done);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Bool(in, \"done\", &v->done);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiHeader(struct MultiHeader*v){\n}\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_Buffer(out, \"auth\", &v->auth);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"auth\", &v->auth);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_AuthPacket(struct AuthPacket*v){\n    deallocate_String(&v->scheme);\n    deallocate_Buffer(&v->auth);\n}\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ReplyHeader(struct ReplyHeader*v){\n}\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataRequest(struct GetDataRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataRequest(struct SetDataRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataResponse(struct SetDataResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*v){\n    deallocate_Buffer(&v->token);\n}\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_ACL_vector(struct ACL_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_ACL(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_ACL(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_ACL(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"flags\", &v->flags);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"flags\", &v->flags);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateRequest(struct CreateRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteRequest(struct DeleteRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*v){\n    deallocate_String(&v->path);\n}\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*v){\n}\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncRequest(struct SyncRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncResponse(struct SyncResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLRequest(struct GetACLRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLRequest(struct SetACLRequest*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLResponse(struct SetACLResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Int(out, \"state\", &v->state);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Int(in, \"state\", &v->state);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_WatcherEvent(struct WatcherEvent*v){\n    deallocate_String(&v->path);\n}\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorResponse(struct ErrorResponse*v){\n}\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateResponse(struct CreateResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsRequest(struct ExistsRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsResponse(struct ExistsResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataResponse(struct GetDataResponse*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*v){\n    deallocate_String_vector(&v->children);\n}\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*v){\n    deallocate_String_vector(&v->children);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLResponse(struct GetACLResponse*v){\n    deallocate_ACL_vector(&v->acl);\n    deallocate_Stat(&v->stat);\n}\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"serverid\", &v->serverid);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"serverid\", &v->serverid);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_LearnerInfo(struct LearnerInfo*v){\n}\nint allocate_Id_vector(struct Id_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Id_vector(struct Id_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Id(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Id(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Id(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Id_vector(out, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Id_vector(in, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_QuorumPacket(struct QuorumPacket*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Id_vector(&v->authinfo);\n}\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"magic\", &v->magic);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Long(out, \"dbid\", &v->dbid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"magic\", &v->magic);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Long(in, \"dbid\", &v->dbid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_FileHeader(struct FileHeader*v){\n}\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"clientId\", &v->clientId);\n    rc = rc ? rc : out->serialize_Int(out, \"cxid\", &v->cxid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Long(out, \"time\", &v->time);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"clientId\", &v->clientId);\n    rc = rc ? rc : in->deserialize_Int(in, \"cxid\", &v->cxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"time\", &v->time);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_TxnHeader(struct TxnHeader*v){\n}\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->serialize_Int(out, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->deserialize_Int(in, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxn(struct CreateTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteTxn(struct DeleteTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataTxn(struct SetDataTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLTxn(struct SetACLTxn*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*v){\n}\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorTxn(struct ErrorTxn*v){\n}\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Txn(struct Txn*v){\n    deallocate_Buffer(&v->data);\n}\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Txn_vector(struct Txn_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Txn(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Txn(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Txn(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Txn_vector(out, \"txns\", &v->txns);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Txn_vector(in, \"txns\", &v->txns);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiTxn(struct MultiTxn*v){\n    deallocate_Txn_vector(&v->txns);\n}\n"
  },
  {
    "path": "cluster/cluster_conn/zookeeper/zookeeper.jute.h",
    "content": "/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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 __ZOOKEEPER_JUTE__\n#define __ZOOKEEPER_JUTE__\n#include \"recordio.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct Id {\n    char * scheme;\n    char * id;\n};\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v);\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v);\nvoid deallocate_Id(struct Id*);\nstruct ACL {\n    int32_t perms;\n    struct Id id;\n};\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v);\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v);\nvoid deallocate_ACL(struct ACL*);\nstruct Stat {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int32_t dataLength;\n    int32_t numChildren;\n    int64_t pzxid;\n};\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v);\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v);\nvoid deallocate_Stat(struct Stat*);\nstruct StatPersisted {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int64_t pzxid;\n};\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v);\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v);\nvoid deallocate_StatPersisted(struct StatPersisted*);\nstruct StatPersistedV1 {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n};\nint serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v);\nint deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v);\nvoid deallocate_StatPersistedV1(struct StatPersistedV1*);\nstruct ConnectRequest {\n    int32_t protocolVersion;\n    int64_t lastZxidSeen;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v);\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v);\nvoid deallocate_ConnectRequest(struct ConnectRequest*);\nstruct ConnectResponse {\n    int32_t protocolVersion;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v);\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v);\nvoid deallocate_ConnectResponse(struct ConnectResponse*);\nstruct String_vector {\n    int32_t count;\n    char * *data;\n\n};\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v);\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v);\nint allocate_String_vector(struct String_vector *v, int32_t len);\nint deallocate_String_vector(struct String_vector *v);\nstruct SetWatches {\n    int64_t relativeZxid;\n    struct String_vector dataWatches;\n    struct String_vector existWatches;\n    struct String_vector childWatches;\n};\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v);\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v);\nvoid deallocate_SetWatches(struct SetWatches*);\nstruct RequestHeader {\n    int32_t xid;\n    int32_t type;\n};\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v);\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v);\nvoid deallocate_RequestHeader(struct RequestHeader*);\nstruct MultiHeader {\n    int32_t type;\n    int32_t done;\n    int32_t err;\n};\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v);\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v);\nvoid deallocate_MultiHeader(struct MultiHeader*);\nstruct AuthPacket {\n    int32_t type;\n    char * scheme;\n    struct buffer auth;\n};\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v);\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v);\nvoid deallocate_AuthPacket(struct AuthPacket*);\nstruct ReplyHeader {\n    int32_t xid;\n    int64_t zxid;\n    int32_t err;\n};\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v);\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v);\nvoid deallocate_ReplyHeader(struct ReplyHeader*);\nstruct GetDataRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v);\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v);\nvoid deallocate_GetDataRequest(struct GetDataRequest*);\nstruct SetDataRequest {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v);\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v);\nvoid deallocate_SetDataRequest(struct SetDataRequest*);\nstruct SetDataResponse {\n    struct Stat stat;\n};\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v);\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v);\nvoid deallocate_SetDataResponse(struct SetDataResponse*);\nstruct GetSASLRequest {\n    struct buffer token;\n};\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v);\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v);\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*);\nstruct SetSASLRequest {\n    struct buffer token;\n};\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v);\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v);\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*);\nstruct SetSASLResponse {\n    struct buffer token;\n};\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v);\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v);\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*);\nstruct ACL_vector {\n    int32_t count;\n    struct ACL *data;\n\n};\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v);\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v);\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len);\nint deallocate_ACL_vector(struct ACL_vector *v);\nstruct CreateRequest {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t flags;\n};\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v);\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v);\nvoid deallocate_CreateRequest(struct CreateRequest*);\nstruct DeleteRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v);\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v);\nvoid deallocate_DeleteRequest(struct DeleteRequest*);\nstruct GetChildrenRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v);\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v);\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*);\nstruct GetChildren2Request {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v);\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v);\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*);\nstruct CheckVersionRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v);\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v);\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*);\nstruct GetMaxChildrenRequest {\n    char * path;\n};\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v);\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v);\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*);\nstruct GetMaxChildrenResponse {\n    int32_t max;\n};\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v);\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v);\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*);\nstruct SetMaxChildrenRequest {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v);\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v);\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*);\nstruct SyncRequest {\n    char * path;\n};\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v);\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v);\nvoid deallocate_SyncRequest(struct SyncRequest*);\nstruct SyncResponse {\n    char * path;\n};\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v);\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v);\nvoid deallocate_SyncResponse(struct SyncResponse*);\nstruct GetACLRequest {\n    char * path;\n};\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v);\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v);\nvoid deallocate_GetACLRequest(struct GetACLRequest*);\nstruct SetACLRequest {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v);\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v);\nvoid deallocate_SetACLRequest(struct SetACLRequest*);\nstruct SetACLResponse {\n    struct Stat stat;\n};\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v);\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v);\nvoid deallocate_SetACLResponse(struct SetACLResponse*);\nstruct WatcherEvent {\n    int32_t type;\n    int32_t state;\n    char * path;\n};\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v);\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v);\nvoid deallocate_WatcherEvent(struct WatcherEvent*);\nstruct ErrorResponse {\n    int32_t err;\n};\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v);\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v);\nvoid deallocate_ErrorResponse(struct ErrorResponse*);\nstruct CreateResponse {\n    char * path;\n};\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v);\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v);\nvoid deallocate_CreateResponse(struct CreateResponse*);\nstruct ExistsRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v);\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v);\nvoid deallocate_ExistsRequest(struct ExistsRequest*);\nstruct ExistsResponse {\n    struct Stat stat;\n};\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v);\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v);\nvoid deallocate_ExistsResponse(struct ExistsResponse*);\nstruct GetDataResponse {\n    struct buffer data;\n    struct Stat stat;\n};\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v);\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v);\nvoid deallocate_GetDataResponse(struct GetDataResponse*);\nstruct GetChildrenResponse {\n    struct String_vector children;\n};\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v);\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v);\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*);\nstruct GetChildren2Response {\n    struct String_vector children;\n    struct Stat stat;\n};\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v);\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v);\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*);\nstruct GetACLResponse {\n    struct ACL_vector acl;\n    struct Stat stat;\n};\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v);\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v);\nvoid deallocate_GetACLResponse(struct GetACLResponse*);\nstruct LearnerInfo {\n    int64_t serverid;\n    int32_t protocolVersion;\n};\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v);\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v);\nvoid deallocate_LearnerInfo(struct LearnerInfo*);\nstruct Id_vector {\n    int32_t count;\n    struct Id *data;\n\n};\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v);\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v);\nint allocate_Id_vector(struct Id_vector *v, int32_t len);\nint deallocate_Id_vector(struct Id_vector *v);\nstruct QuorumPacket {\n    int32_t type;\n    int64_t zxid;\n    struct buffer data;\n    struct Id_vector authinfo;\n};\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v);\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v);\nvoid deallocate_QuorumPacket(struct QuorumPacket*);\nstruct FileHeader {\n    int32_t magic;\n    int32_t version;\n    int64_t dbid;\n};\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v);\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v);\nvoid deallocate_FileHeader(struct FileHeader*);\nstruct TxnHeader {\n    int64_t clientId;\n    int32_t cxid;\n    int64_t zxid;\n    int64_t time;\n    int32_t type;\n};\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v);\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v);\nvoid deallocate_TxnHeader(struct TxnHeader*);\nstruct CreateTxnV0 {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n};\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v);\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v);\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*);\nstruct CreateTxn {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n    int32_t parentCVersion;\n};\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v);\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v);\nvoid deallocate_CreateTxn(struct CreateTxn*);\nstruct DeleteTxn {\n    char * path;\n};\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v);\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v);\nvoid deallocate_DeleteTxn(struct DeleteTxn*);\nstruct SetDataTxn {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v);\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v);\nvoid deallocate_SetDataTxn(struct SetDataTxn*);\nstruct CheckVersionTxn {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v);\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v);\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*);\nstruct SetACLTxn {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v);\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v);\nvoid deallocate_SetACLTxn(struct SetACLTxn*);\nstruct SetMaxChildrenTxn {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v);\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v);\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*);\nstruct CreateSessionTxn {\n    int32_t timeOut;\n};\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v);\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v);\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*);\nstruct ErrorTxn {\n    int32_t err;\n};\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v);\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v);\nvoid deallocate_ErrorTxn(struct ErrorTxn*);\nstruct Txn {\n    int32_t type;\n    struct buffer data;\n};\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v);\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v);\nvoid deallocate_Txn(struct Txn*);\nstruct Txn_vector {\n    int32_t count;\n    struct Txn *data;\n\n};\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v);\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v);\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len);\nint deallocate_Txn_vector(struct Txn_vector *v);\nstruct MultiTxn {\n    struct Txn_vector txns;\n};\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v);\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v);\nvoid deallocate_MultiTxn(struct MultiTxn*);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif //ZOOKEEPER_JUTE__\n"
  },
  {
    "path": "cluster/qedis_proxy/CMakeLists.txt",
    "content": "INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon)\n\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ananas)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cluster_conn)\n\nAUX_SOURCE_DIRECTORY(. PROXY_SRC)\nADD_EXECUTABLE(qedisproxy ${PROXY_SRC})\n\nSET(EXECUTABLE_OUTPUT_PATH  ${PROJECT_SOURCE_DIR}/bin/)\n\nTARGET_LINK_LIBRARIES(qedisproxy ananas;zk)\n\nADD_DEPENDENCIES(qedisproxy ananas; zk)\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClientConn.cc",
    "content": "#include <cassert>\n#include <algorithm>\n\n#include \"ProxyLog.h\"\n#include \"Command.h\"\n#include \"ClientConn.h\"\n\n#include \"ClusterManager.h\"\n#include \"QedisManager.h\"\n#include \"QedisConn.h\"\n\n#include \"net/EventLoop.h\"\n#include \"util/Util.h\"\n\nstatic \nananas::PacketLen_t ProcessInlineCmd(const char* buf,\n                                     size_t bytes,\n                                     std::vector<std::string>& params)\n{\n    if (bytes < 2)\n        return 0;\n\n    std::string res;\n\n    for (size_t i = 0; i + 1 < bytes; ++ i)\n    {\n        if (buf[i] == '\\r' && buf[i+1] == '\\n')\n        {\n            if (!res.empty())\n                params.emplace_back(std::move(res));\n\n            return static_cast<ananas::PacketLen_t>(i + 2);\n        }\n\n        if (isblank(buf[i]))\n        {\n            if (!res.empty())\n            {\n                params.reserve(4);\n                params.emplace_back(std::move(res));\n            }\n        }\n        else\n        {\n            res.reserve(16);\n            res.push_back(buf[i]);\n        }\n    }\n\n    return 0;\n}\n\n\nClientConn::ClientConn(ananas::Connection* conn) :\n    hostConn_(conn)\n{\n}\n\nananas::PacketLen_t ClientConn::OnRecv(ananas::Connection* conn, const char* data, ananas::PacketLen_t len)\n{\n    assert (conn == hostConn_);\n\n    const char* const end = data + len;\n    const char* ptr = data;\n\n    auto parseRet = proto_.ParseRequest(ptr, end);\n    if (parseRet == ParseResult::error)\n    {   \n        if (!proto_.IsInitialState())\n        {\n            ERR(g_logger) << \"ParseError for \" << data;\n            hostConn_->ActiveClose();\n            return 0;\n        }\n\n        // try inline command\n        std::vector<std::string> params;\n        auto consumed = ProcessInlineCmd(ptr, len, params); \n        if (consumed == 0)\n            return 0;\n\n        proto_.GetParams() = params;\n        proto_.GetRawRequest().assign(ptr, ptr + consumed);\n\n        ptr += consumed;\n        parseRet = ParseResult::ok;\n    }\n    \n    if (parseRet != ParseResult::ok) \n    { \n        return static_cast<ananas::PacketLen_t>(ptr - data); \n    }\n    \n    ANANAS_DEFER {\n        proto_.Reset();\n    };\n\n    // handle request packet\n    const auto& params = proto_.GetParams();\n    assert (!params.empty());\n\n    std::string cmd(params[0]);\n    std::transform(params[0].begin(), params[0].end(), cmd.begin(), ::tolower);\n\n    const CommandInfo* info = CommandTable::GetCommandInfo(cmd);\n    if (!info)\n    {\n        const auto& e = g_errorInfo[QError_unknowCmd];\n        hostConn_->SendPacket(e.errorStr, e.len);\n        return static_cast<ananas::PacketLen_t>(ptr - data); \n    }\n\n    if (info->handler)\n    {\n        std::string reply(info->handler(params));\n        hostConn_->SendPacket(reply.data(), reply.size());\n        return static_cast<ananas::PacketLen_t>(ptr - data); \n    }\n\n    const auto& host = ClusterManager::Instance().GetServer(params[1]);\n    if (host.empty())\n    {\n        const auto& e = g_errorInfo[QError_notready];\n        hostConn_->SendPacket(e.errorStr, e.len);\n        return static_cast<ananas::PacketLen_t>(ptr - data); \n    }\n            \n    // Forwart request to Qedis `host`\n    const auto& rawReq = proto_.GetRawRequest();\n    QedisManager::Instance().GetConnection(host)\n        .Then([rawReq, conn = hostConn_](ananas::Try<QedisConn* >&& qconn) {\n            try {\n                QedisConn* qc = qconn;\n                return qc->ForwardRequest(rawReq);\n            }\n            catch (const std::exception& e) {\n                ERR(g_logger) << \"GetServer with exception \" << e.what();\n                const auto& info = g_errorInfo[QError_dead];\n                return ananas::MakeReadyFuture<std::string>(std::string(info.errorStr, info.len));\n            }\n        })\n        .Then([conn](const std::string& reply) {\n            DBG(g_logger) << \"Send reply \" << reply;\n            conn->SendPacket(reply.data(), reply.size());\n        });\n\n    return static_cast<ananas::PacketLen_t>(ptr - data);\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClientConn.h",
    "content": "#ifndef BERT_CLIENTCONN_H\n#define BERT_CLIENTCONN_H\n\n#include <queue>\n#include <string>\n#include <vector>\n#include <unordered_map>\n\n#include \"net/Connection.h\"\n#include \"Protocol.h\"\n\nclass ClientConn final\n{\npublic:\n    explicit\n    ClientConn(ananas::Connection* conn);\n\n    ananas::PacketLen_t OnRecv(ananas::Connection* conn, const char* data, ananas::PacketLen_t len);\n\nprivate:\n    ServerProtocol proto_;\n    ananas::Connection* hostConn_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClientManager.cc",
    "content": "#include \"ClientManager.h\"\n#include \"ClientConn.h\"\n#include \"ProxyLog.h\"\n\n#include \"net/EventLoop.h\"\n#include \"net/Connection.h\"\n#include \"net/Socket.h\"\n\nClientManager& ClientManager::Instance()\n{\n    static ClientManager mgr;\n    return mgr;\n}\n\nClientManager::ClientManager() :\n    loop_(nullptr),\n    listening_(false)\n{\n}\n\nvoid ClientManager::SetEventLoop(ananas::EventLoop* loop)\n{\n    assert (!loop_);\n    loop_ = loop;\n}\n\nbool ClientManager::Listen(const std::string& addr)\n{\n    assert (!listening_);\n    listening_ = loop_->Listen(ananas::SocketAddr(addr),\n                               std::bind(&ClientManager::OnNewConnection, this, std::placeholders::_1));\n    return listening_;\n}\n\nvoid ClientManager::OnNewConnection(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClientManager::OnNewConnection \" << conn->Identifier();\n\n    auto ctx = std::make_shared<ClientConn>(conn);\n\n    conn->SetUserData(ctx);\n    conn->SetMinPacketSize(4);\n    conn->SetOnMessage(std::bind(&ClientConn::OnRecv,\n                                 ctx.get(),\n                                 std::placeholders::_1,\n                                 std::placeholders::_2,\n                                 std::placeholders::_3));\n    conn->SetOnConnect(std::bind(&ClientManager::OnConnect, this, std::placeholders::_1));\n    conn->SetOnDisconnect(std::bind(&ClientManager::OnDisconnect, this, std::placeholders::_1));\n}\n\nvoid ClientManager::OnConnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClientManager::OnConnect \" << conn->Identifier();\n}\n\nvoid ClientManager::OnDisconnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClientManager::OnDisconnect \" << conn->Identifier();\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClientManager.h",
    "content": "#ifndef BERT_CLIENTMANAGER_H\n#define BERT_CLIENTMANAGER_H\n\n#include <unordered_map>\n#include <vector>\n#include <string>\n\nnamespace ananas\n{\n    class EventLoop;\n    class Connection;\n}\n\nclass ClientManager\n{\npublic:\n    static ClientManager& Instance();\n\n    ClientManager();\n\n    void SetEventLoop(ananas::EventLoop* loop);\n    bool Listen(const std::string& addr);\n\n    void OnNewConnection(ananas::Connection* conn);\n    void OnConnect(ananas::Connection* conn);\n    void OnDisconnect(ananas::Connection* conn);\n\nprivate:\n    //std::unordered_map<int, ananas::Connection* > connMap_;\n\n    ananas::EventLoop* loop_;\n    bool listening_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClusterManager.cc",
    "content": "#include \"ClusterManager.h\"\n#include \"ProxyLog.h\"\n\n#include \"net/EventLoop.h\"\n#include \"net/Connection.h\"\n#include \"net/Socket.h\"\n\n#if USE_ZOOKEEPER\n    #include \"ZookeeperConn.h\"\n#else\n    #error \"Please define USE_ZOOKEEPER to non-zero\"\n#endif\n\n\nClusterManager& ClusterManager::Instance()\n{\n    static ClusterManager mgr;\n    return mgr;\n}\n\nClusterManager::ClusterManager() :\n    loop_(nullptr)\n{\n}\n\nvoid ClusterManager::SetEventLoop(ananas::EventLoop* loop)\n{\n    assert (!loop_);\n    loop_ = loop;\n}\n\nbool ClusterManager::Connect()\n{\n    if (cluster_.empty())\n        return false;\n\n    index_ ++;\n    if (index_ >= static_cast<int>(cluster_.size()))\n        index_ = 0;\n\n    const ananas::SocketAddr& addr = cluster_[index_];\n    return loop_->Connect(addr,\n                          std::bind(&ClusterManager::OnNewConnection, this, std::placeholders::_1),\n                          std::bind(&ClusterManager::OnConnFail, this, std::placeholders::_1, std::placeholders::_2), \n                          ananas::DurationMs(1000));\n}\n\nvoid ClusterManager::AddClusterAddr(const std::string& host)\n{\n    ananas::SocketAddr addr(host);\n    cluster_.push_back(addr);\n}\n\nvoid ClusterManager::OnNewConnection(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClusterManager::OnNewConnection \" << conn->Identifier();\n\n#if USE_ZOOKEEPER\n    auto ctx = std::make_shared<ZookeeperConn>(conn);\n#else\n    #error \"Please define USE_ZOOKEEPER to non-zero\"\n#endif\n\n    using ananas::PacketLen_t;\n    using ananas::Connection;\n\n    conn->SetUserData(ctx);\n    conn->SetMinPacketSize(4);\n    conn->SetOnMessage([ctx](Connection* c, const char* data, PacketLen_t len) {\n         const char* ptr = data;\n         if (!ctx->OnData(ptr, len)) {\n             c->ActiveClose();\n         }\n\n         return static_cast<PacketLen_t>(ptr - data);\n    });\n    conn->SetOnConnect(std::bind(&ClusterManager::OnConnect, this, std::placeholders::_1));\n    conn->SetOnDisconnect(std::bind(&ClusterManager::OnDisconnect, this, std::placeholders::_1));\n}\n\nvoid ClusterManager::OnConnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClusterManager::OnConnect \" << conn->Identifier();\n\n    auto ctx = conn->GetUserData<qedis::ClusterConn>();\n    ctx->OnConnect();\n}\n\nvoid ClusterManager::OnDisconnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"ClusterManager::OnDisconnect \" << conn->Identifier();\n\n    shardingInfo_.clear();\n    hostInfo_.clear();\n\n    auto ctx = conn->GetUserData<qedis::ClusterConn>();\n    ctx->OnDisconnect();\n}\n\nvoid ClusterManager::OnConnFail(ananas::EventLoop* loop, const ananas::SocketAddr& peer)\n{\n    INF(g_logger) << \"ClusterManager::OnConnFail \" << peer.ToString();\n\n    loop_->Sleep(ananas::DurationMs(2000))\n          .Then(std::bind(&ClusterManager::Connect, this));\n}\n\nvoid ClusterManager::AddShardingInfo(int setid, const std::vector<std::string>& shardings)\n{\n    for (const auto& shard : shardings)\n    {\n        int id = std::stoi(shard);\n        bool succ = shardingInfo_.insert({id, setid}).second;\n        if (succ)\n            DBG(g_logger) << \"SUCC: Insert sharding info \" << id << \":\" << setid;\n        else\n            DBG(g_logger) << \"FAILED: Insert sharding info \" << id << \":\" << setid;\n    }\n}\n\nvoid ClusterManager::AddServerInfo(int setid, const std::string& host)\n{\n    auto& svrlist = hostInfo_[setid];\n    svrlist.push_back(host);\n    DBG(g_logger) << \"AddServerInfo : \" << setid << \":\" << host;\n}\n\n\nconst std::string& ClusterManager::GetServer(const std::string& key) const\n{\n    // TODO calc key's hash value\n    const int hash = 1;\n\n    auto it = shardingInfo_.find(hash);\n    if (it != shardingInfo_.end())\n    {\n        auto itHost  = hostInfo_.find(it->second);\n        if (itHost != hostInfo_.end())\n        {\n            return itHost->second[0]; // return the first addr\n        }\n    }\n\n    static const std::string kEmpty;\n    return kEmpty;\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ClusterManager.h",
    "content": "#ifndef BERT_CLUSTERMANAGER_H\n#define BERT_CLUSTERMANAGER_H\n\n#include <unordered_map>\n#include <vector>\n#include <string>\n\nnamespace ananas\n{\n    class EventLoop;\n    class Connection;\n    struct SocketAddr;\n}\n\nclass ClusterManager\n{\npublic:\n    static ClusterManager& Instance();\n\n    ClusterManager();\n\n    void SetEventLoop(ananas::EventLoop* loop);\n    void AddClusterAddr(const std::string& host);\n    bool Connect();\n\n    void OnNewConnection(ananas::Connection* conn);\n    void OnConnect(ananas::Connection* conn);\n    void OnDisconnect(ananas::Connection* conn);\n    void OnConnFail(ananas::EventLoop* loop, const ananas::SocketAddr& peer);\n\n    void AddShardingInfo(int setid, const std::vector<std::string>& shardings);\n    void AddServerInfo(int setid, const std::string& host);\n\n    const std::string& GetServer(const std::string& key) const;\n\nprivate:\n    int index_ = -1;\n    std::vector<ananas::SocketAddr> cluster_;\n\n    // sharding-id -> set-id\n    std::unordered_map<int, int> shardingInfo_;\n\n    // set-id -> server-list\n    std::unordered_map<int, std::vector<std::string>> hostInfo_;\n\n    ananas::EventLoop* loop_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/Command.cc",
    "content": "#include \"Command.h\"\n\nconst CommandInfo CommandTable::s_info[] =\n{\n    // key\n    {\"type\",        Attr_read,                2,  },\n    {\"exists\",      Attr_read,                2,  },\n    {\"del\",         Attr_write,              -2,  },\n    {\"expire\",      Attr_read,                3,  },\n    {\"ttl\",         Attr_read,                2,  },\n    {\"pexpire\",     Attr_read,                3,  },\n    {\"pttl\",        Attr_read,                2,  },\n    {\"expireat\",    Attr_read,                3,  },\n    {\"pexpireat\",   Attr_read,                3,  },\n    {\"persist\",     Attr_read,                2, },\n\n    // local\n    {\"ping\",        Attr_read,                1,  &ping},\n    {\"info\",        Attr_read,               -1,  &info},\n    \n    // string\n    {\"strlen\",      Attr_read,                2,  },\n    {\"set\",         Attr_write,               3,  },\n    //{\"mset\",        Attr_write,              -3,  },\n    //{\"msetnx\",      Attr_write,              -3,  },\n    {\"setnx\",       Attr_write,               3,  },\n    {\"setex\",       Attr_write,               4,  },\n    {\"psetex\",      Attr_write,               4,  },\n    {\"get\",         Attr_read,                2,  },\n    {\"getset\",      Attr_write,               3,  },\n    //{\"mget\",        Attr_read,               -2,  &mget},\n    {\"append\",      Attr_write,               3,  },\n    {\"bitcount\",    Attr_read,               -2,  },\n    {\"bitop\",       Attr_write,              -4,  },\n    {\"getbit\",      Attr_read,                3,  },\n    {\"setbit\",      Attr_write,               4,  },\n    {\"incr\",        Attr_write,               2,  },\n    {\"decr\",        Attr_write,               2,  },\n    {\"incrby\",      Attr_write,               3,  },\n    {\"incrbyfloat\", Attr_write,               3,  },\n    {\"decrby\",      Attr_write,               3,  },\n    {\"getrange\",    Attr_read,                4,  },\n    {\"setrange\",    Attr_write,               4,  },\n\n    // list\n    {\"lpush\",       Attr_write,              -3,  },\n    {\"rpush\",       Attr_write,              -3,  },\n    {\"lpushx\",      Attr_write,              -3,  },\n    {\"rpushx\",      Attr_write,              -3,  },\n    {\"lpop\",        Attr_write,               2,  },\n    {\"rpop\",        Attr_write,               2,  },\n    {\"lindex\",      Attr_read,                3,  },\n    {\"llen\",        Attr_read,                2,  },\n    {\"lset\",        Attr_write,               4,  },\n    {\"ltrim\",       Attr_write,               4,  },\n    {\"lrange\",      Attr_read,                4,  },\n    {\"linsert\",     Attr_write,               5,  },\n    {\"lrem\",        Attr_write,               4,  },\n\n    // hash\n    {\"hget\",        Attr_read,                3,  },\n    {\"hgetall\",     Attr_read,                2,  },\n    {\"hmget\",       Attr_read,               -3,  },\n    {\"hset\",        Attr_write,               4,  },\n    {\"hsetnx\",      Attr_write,               4,  },\n    {\"hmset\",       Attr_write,              -4,  },\n    {\"hlen\",        Attr_read,                2,  },\n    {\"hexists\",     Attr_read,                3,  },\n    {\"hkeys\",       Attr_read,                2,  },\n    {\"hvals\",       Attr_read,                2,  },\n    {\"hdel\",        Attr_write,              -3,  },\n    {\"hincrby\",     Attr_write,               4,  },\n    {\"hincrbyfloat\",Attr_write,               4,  },\n    {\"hscan\",       Attr_read,               -3,  },\n    {\"hstrlen\",     Attr_read,                3,  },\n\n    // set\n    {\"sadd\",        Attr_write,              -3,  },\n    {\"scard\",       Attr_read,                2,  },\n    {\"sismember\",   Attr_read,                3,  },\n    {\"srem\",        Attr_write,              -3,  },\n    {\"smembers\",    Attr_read,                2,  },\n    //{\"sdiff\",       Attr_read,               -2,  },\n    //{\"sdiffstore\",  Attr_write,              -3,  &sdiffstore},\n    //{\"sinter\",      Attr_read,               -2,  &sinter},\n    //{\"sinterstore\", Attr_write,              -3,  &sinterstore},\n    //{\"sunion\",      Attr_read,               -2,  &sunion},\n    //{\"sunionstore\", Attr_write,              -3,  &sunionstore},\n    //{\"smove\",       Attr_write,               4,  &smove},\n    {\"spop\",        Attr_write,               2,  },\n    {\"srandmember\", Attr_read,                2,  },\n    {\"sscan\",       Attr_read,               -3,  },\n\n    //\n    {\"zadd\",        Attr_write,              -4,  },\n    {\"zcard\",       Attr_read,                2,  },\n    {\"zrank\",       Attr_read,                3,  },\n    {\"zrevrank\",    Attr_read,                3,  },\n    {\"zrem\",        Attr_write,              -3,  },\n    {\"zincrby\",     Attr_write,               4,  },\n    {\"zscore\",      Attr_read,                3,  },\n    {\"zrange\",      Attr_read,               -4,  },\n    {\"zrevrange\",   Attr_read,               -4,  },\n    {\"zrangebyscore\",   Attr_read,           -4,  },\n    {\"zrevrangebyscore\",Attr_read,           -4,  },\n    {\"zremrangebyrank\", Attr_write,           4,  },\n    {\"zremrangebyscore\",Attr_write,           4,  },\n};\n    \n\nstd::map<std::string, const CommandInfo*>  CommandTable::s_handlers;\n\nvoid CommandTable::Init()\n{\n    for (const auto& info : s_info)\n    {\n        s_handlers[info.cmd] = &info;\n    }\n}\n\nconst CommandInfo* CommandTable::GetCommandInfo(const std::string& cmd)\n{\n    auto it(s_handlers.find(cmd));\n    if (it != s_handlers.end())\n    {\n        return it->second;\n    }\n    \n    return 0;\n}\n\nstruct QedisErrorInfo g_errorInfo[] = {\n    {sizeof \"+OK\\r\\n\"- 1, \"+OK\\r\\n\"},\n    {sizeof \"-ERR wrong number of arguments\\r\\n\"- 1, \"-ERR wrong number of arguments\\r\\n\"},\n    {sizeof \"-ERR Unknown command\\r\\n\"- 1,   \"-ERR Unknown command\\r\\n\"},\n    {sizeof \"-ERR syntax error\\r\\n\"-1, \"-ERR syntax error\\r\\n\"},\n    {sizeof \"-ERR Server not ready\\r\\n\"-1, \"-ERR Server not ready\\r\\n\"},\n    {sizeof \"-ERR Server response timeout\\r\\n\"-1, \"-ERR Server response timeout\\r\\n\"},\n    {sizeof \"-ERR Server not alive\\r\\n\"-1, \"-ERR Server not alive\\r\\n\"},\n};\n\nstd::string ping(const std::vector<std::string>& params)\n{\n    return std::string(\"+PONG\\r\\n\");\n}\n\nstd::string info(const std::vector<std::string>& params)\n{\n    return std::string(\"$9\\r\\nTODO_INFO\\r\\n\");\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/Command.h",
    "content": "#ifndef BERT_COMMAND_H\n#define BERT_COMMAND_H\n\n#include <vector>\n#include <map>\n#include <string>\n\nenum CommandAttr\n{\n    Attr_read  = 0x1,\n    Attr_write = 0x1 << 1,\n    Attr_multikey = 0x1 << 2,\n};\n\n\nusing CommandHandler = std::string (const std::vector<std::string>& params);\n\n// proxy commands\nCommandHandler  ping;\nCommandHandler  info;\n\nstruct CommandInfo\n{\n    std::string cmd;\n    int attr;\n    int params;\n    CommandHandler* handler;\n\n    CommandInfo(const std::string& c, int a, int p, CommandHandler* ch = nullptr) :\n        cmd(c),\n        attr(a),\n        params(p),\n        handler(ch)\n    {\n    }\n\n    bool CheckParamsCount(int nParams) const;\n};\n\nclass CommandTable\n{\npublic:\n    static void Init();\n    static const CommandInfo* GetCommandInfo(const std::string& cmd);\n\nprivate:\n    static const CommandInfo s_info[];\n    static std::map<std::string, const CommandInfo* > s_handlers;\n};\n\nenum QedisError\n{\n    QError_nop       = -1,\n    QError_ok        = 0,\n    QError_param     = 1,\n    QError_unknowCmd = 2,\n    QError_syntax    = 3,\n    QError_notready  = 4,\n    QError_timeout   = 5,\n    QError_dead      = 6,\n    QError_max,\n};\n\nextern struct QedisErrorInfo\n{\n    size_t len;\n    const char* errorStr;\n} g_errorInfo[];\n\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ConfigParser.cc",
    "content": "#include <vector>\n#include <fstream>\n#include <memory>\n#include \"ConfigParser.h\"\n\nstatic const int  SPACE     = ' ';\nstatic const int  TAB       = '\\t';\nstatic const int  NEWLINE   = '\\n';\nstatic const int  COMMENT   = '#';\n\n\nstatic size_t  SkipBlank(const char* data, size_t len, size_t off)\n{\n    while (++ off < len)\n    {\n        if (SPACE != data[off] && TAB != data[off])\n        {\n            -- off;\n            break;\n        }\n    }\n\n    return off;\n}\n\nbool ConfigParser::Load(const char* fileName)\n{\n    std::ifstream ifs(fileName, std::ifstream::binary);\n    if (!ifs)\n        return false; // no such file\n\n    std::filebuf* fbuf = ifs.rdbuf();\n    const std::size_t size = fbuf->pubseekoff (0, ifs.end, ifs.in);\n    fbuf->pubseekpos (0, ifs.in);\n      \n    // allocate memory to hold file data\n    std::unique_ptr<char []> data(new char[size]);\n    fbuf->sgetn(data.get(), size);\n    ifs.close();\n\n    data_.clear();\n\n    bool bReadKey = true;\n    std::string key, value;\n    key.reserve(64);\n    value.reserve(64);\n\n    size_t off = 0;\n    while (off < size)\n    {\n        switch (data[off])\n        {\n            case COMMENT:\n                while (++ off < size)\n                {\n                    if (NEWLINE == data[off])\n                    {\n                        -- off;\n                        break;\n                    }\n                }\n                break;\n\n            case NEWLINE:\n                bReadKey = true;\n\n                if (!key.empty())\n                {\n                    data_[key].push_back(value);\n                    \n                    key.clear();\n                    value.clear();\n                }\n                \n                off = SkipBlank(data.get(), size, off);\n                break;\n            \n            case SPACE:\n            case TAB:\n                // 支持value中有空格\n                if (bReadKey)\n                {\n                    bReadKey = false;\n                    off = SkipBlank(data.get(), size, off); // 跳过所有分界空格\n                }\n                else\n                {\n                    value += data[off];\n                }\n                break;\n            \n            case '\\r':\n                break;\n            \n            default:\n                if (bReadKey)\n                    key += data[off];\n                else\n                    value += data[off];\n\n                break;\n        }\n\n        ++ off;\n    }\n    \n    return true;\n}\n\nconst std::vector<std::string>& ConfigParser::GetDataVector(const char* key) const\n{\n    auto it = data_.find(key);\n    if (it == data_.end())\n    {\n        static const std::vector<std::string> kEmpty;\n        return kEmpty;\n    }\n    \n    return it->second;\n}\n\n#ifdef CONFIG_DEBUG\nint main()\n{\n    ConfigParser   csv;\n    csv.Load(\"config\");\n    csv.Print();\n\t\n    std::cout << \"=====================\" << std::endl;\n}\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ConfigParser.h",
    "content": "#ifndef BERT_CONFIGPARSER_H\n#define BERT_CONFIGPARSER_H\n\n#include <map>\n#include <string>\n#include <sstream>\n\n#ifdef CONFIG_DEBUG\n#include <iostream>\n#endif\n\nclass ConfigParser\n{\npublic:\n    bool Load(const char* FileName);\n\n    template <typename T>\n    T GetData(const char* key, const T& default_ = T()) const;\n\n    const std::vector<std::string>& GetDataVector(const char* key) const;\n    \n\n#ifdef CONFIG_DEBUG\n    void Print()\n    {\n        std::cout << \"//////////////////\"<< std::endl;\n        Data::const_iterator it = data_.begin();\n        while (it != data_.end())\n        {\n            std::cout << it->first << \":\" << it->second[0] << \"\\n\";\n            ++ it;\n        }\n    }\n#endif\n\nprivate:\n    typedef std::map<std::string, std::vector<std::string> > Data;\n    \n    Data data_;\n\n    template <typename T>\n    T  _ToType(const std::string& data) const;\n};\n\n\ntemplate <typename T>\ninline T ConfigParser::_ToType(const std::string& data) const\n{\n    T t;\n    std::istringstream os(data);\n    os >> t;\n    return t;\n}\n\ntemplate <>\ninline const char* ConfigParser::_ToType<const char* >(const std::string& data) const\n{\n    return data.c_str();\n}\n\ntemplate <>\ninline std::string ConfigParser::_ToType<std::string >(const std::string& data) const\n{\n    return data;\n}\n\n\ntemplate <typename T>\ninline T ConfigParser::GetData(const char* key, const T& default_) const\n{\n    auto it = data_.find(key);\n    if (it == data_.end())\n        return default_;\n\n    return _ToType<T>(it->second[0]); // only return first value\n}\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/Protocol.cc",
    "content": "\n#include <cassert>\n#include <algorithm>\n#include \"Protocol.h\"\n\n// 1 request -> multi strlist\n// 2 multi -> * number crlf\n// 3 strlist -> str strlist | empty\n// 4 str -> strlen strval\n// 5 strlen -> $ number crlf\n// 6 strval -> string crlf\n\n#define CRLF \"\\r\\n\"\n\n// helpers\nstatic\nconst char* Strstr(const char* ptr, size_t nBytes, const char* pattern, size_t nBytes2)\n{\n    if (!pattern || *pattern == 0)\n        return nullptr;\n            \n    const char* ret = std::search(ptr, ptr + nBytes, pattern, pattern + nBytes2);\n    return ret == ptr + nBytes ? nullptr : ret;\n}\n\nstatic\nconst char* SearchCRLF(const char* ptr, size_t nBytes)\n{\n    return Strstr(ptr, nBytes, CRLF, 2);\n}\n\nstatic\nParseResult GetIntUntilCRLF(const char*& ptr, std::size_t nBytes, int& val)\n{\n    if (nBytes < 3)\n        return ParseResult::wait;\n    \n    std::size_t i = 0;\n    bool negtive = false;\n    if (ptr[0] == '-')\n    {\n        negtive = true;\n        ++ i;\n    }\n    else if (ptr[0] == '+')\n    {\n        ++ i;\n    }\n    \n    int value = 0;\n    for (; i < nBytes; ++ i)\n    {\n        if (isdigit(ptr[i]))\n        {\n            value *= 10;\n            value += ptr[i] - '0';\n        }\n        else\n        {\n            if (ptr[i] != '\\r' || (i+1 < nBytes && ptr[i+1] != '\\n'))\n                return ParseResult::error;\n            \n            if (i + 1 == nBytes)\n                return ParseResult::wait;\n            \n            break;\n        }\n    }\n    \n    if (negtive)\n        value *= -1;\n    \n    ptr += i;\n    ptr += 2;\n    val = value;\n    return ParseResult::ok;\n}\n\n\nvoid ServerProtocol::Reset()\n{\n    multi_ = -1;\n    paramLen_ = -1;\n    params_.clear();\n    content_.clear();\n    nParams_ = 0;\n}\n\nParseResult ServerProtocol::ParseRequest(const char*& ptr, const char* end)\n{\n    if (multi_ == -1)\n    {\n        const char* const start = ptr;\n        auto parseRet = _ParseMulti(ptr, end, multi_);\n        if (parseRet == ParseResult::error ||\n            multi_ < -1)\n            return ParseResult::error;\n\n        if (parseRet != ParseResult::ok)\n            return ParseResult::wait;\n\n        content_.append(start, ptr);\n    }\n\n    return _ParseStrlist(ptr, end, params_);\n}\n\nParseResult ServerProtocol::_ParseMulti(const char*& ptr, const char* end, int& result)\n{\n    if (end - ptr < 3)\n        return ParseResult::wait;\n\n    if (*ptr != '*')\n        return ParseResult::error;\n\n    ++ ptr;\n\n    return GetIntUntilCRLF(ptr,  end - ptr, result);\n}\n\nParseResult ServerProtocol::_ParseStrlist(const char*& ptr, const char* end, std::vector<std::string>& results)\n{\n    while (nParams_ < multi_)\n    {\n        std::string res;\n        auto parseRet = _ParseStr(ptr, end, res);\n\n        if (parseRet == ParseResult::ok)\n        {\n            results.emplace_back(std::move(res));\n            ++ nParams_;\n        }\n        else\n        {\n            return parseRet;\n        }\n    }\n\n    return ParseResult::ok;\n}\n\nParseResult ServerProtocol::_ParseStr(const char*& ptr, const char* end, std::string& res)\n{\n    if (paramLen_ == -1)\n    {\n        const char* const start = ptr;\n        auto parseRet = _ParseStrlen(ptr, end, paramLen_);\n        if (parseRet == ParseResult::error ||\n            paramLen_ < -1)\n            return ParseResult::error;\n\n        if (parseRet != ParseResult::ok)\n            return ParseResult::wait;\n\n        content_.append(start, ptr);\n    }\n\n    if (paramLen_ == -1)\n    {\n        res.clear();\n        return ParseResult::ok;\n    }\n    else\n    {\n        return _ParseStrval(ptr, end, res);\n    }\n}\n\nParseResult ServerProtocol::_ParseStrval(const char*& ptr, const char* end, std::string& res)\n{\n    assert (paramLen_ >= 0);\n\n    if (static_cast<int>(end - ptr) < paramLen_ + 2)\n        return ParseResult::wait;\n\n    const char* const start = ptr;\n    auto tail = ptr + paramLen_;\n    if (tail[0] != '\\r' || tail[1] != '\\n')\n        return ParseResult::error;\n\n    res.assign(ptr, tail - ptr);\n    ptr = tail + 2;\n    paramLen_ = -1;\n\n    content_.append(start, ptr);\n    return ParseResult::ok;\n}\n\nParseResult ServerProtocol::_ParseStrlen(const char*& ptr, const char* end, int& result)\n{\n    if (end - ptr < 3)\n        return ParseResult::wait;\n\n    if (*ptr != '$')\n        return ParseResult::error;\n\n    ++ ptr;\n\n    const auto ret = GetIntUntilCRLF(ptr,  end - ptr, result);\n    if (ret != ParseResult::ok)\n        -- ptr;\n\n    return ret;\n}\n\n// ClientProtocol\nClientProtocol::ClientProtocol()\n{\n    Reset();\n}\n    \nParseResult ClientProtocol::Parse(const char*& ptr, const char* end)\n{\n    assert (end - ptr >= 1);\n\n    const char* data = ptr;\n    if (type_ == ResponseType::None)\n    {\n        switch (data[0])\n        {\n            case '+':\n                type_ = ResponseType::Fine;\n                break;\n\n            case '-':\n                type_ = ResponseType::Error;\n                break;\n\n            case '$':\n                type_ = ResponseType::String;\n                break;\n\n            case ':':\n                type_ = ResponseType::Number;\n                break;\n\n            case '*':\n                type_ = ResponseType::Multi;\n                break;\n\n            default:\n                return ParseResult::error;\n        }\n    }\n\n    bool ready = false;\n    switch (type_)\n    {\n        case ResponseType::Fine:\n        case ResponseType::Error:\n        case ResponseType::Number:\n            {\n                const char* res = SearchCRLF(data + 1, end - (data + 1));\n                if (res)\n                {\n                    data = res + 2;\n                    ready = true;\n                }\n                else\n                {\n                    return ParseResult::wait;\n                }\n            }\n            break;\n\n        case ResponseType::String:\n            {\n                auto ret = _ParseStr(data, end);\n                if (ret == ParseResult::error)\n                    return ret;\n                else if (ret == ParseResult::ok)\n                    ready = true;\n            }\n            \n            break;\n\n        case ResponseType::Multi:\n            {\n                auto ret = _ParseMulti(data, end);\n                if (ret == ParseResult::error)\n                    return ret;\n                else if (ret == ParseResult::ok)\n                    ready = true;\n            }\n            break;\n\n        default:\n            assert (!!!\"bug wrong type\");\n            break;\n    }\n\n    if (ready)\n    {\n        content_.append(ptr, data);\n        ptr = data;\n    }\n\n    return ParseResult::ok;\n}\n\nvoid ClientProtocol::Reset()\n{\n    type_ = ResponseType::None;\n    content_.clear();\n    multi_ = kInvalid;\n    paramLen_ = kInvalid;\n    nParams_ = 0;\n}\n\nParseResult ClientProtocol::_ParseStr(const char*& ptr, const char* end)\n{\n    if (paramLen_ == kInvalid)\n    {\n        auto parseRet = _ParseStrlen(ptr, end, paramLen_);\n        if (parseRet == ParseResult::error ||\n            paramLen_ < -1)\n            return ParseResult::error;\n\n        if (parseRet != ParseResult::ok)\n            return ParseResult::wait;\n    }\n\n    if (paramLen_ == -1)\n        return ParseResult::ok;\n    else\n        return _ParseStrval(ptr, end);\n}\n\nParseResult ClientProtocol::_ParseStrval(const char*& ptr, const char* end)\n{\n    assert (paramLen_ >= 0);\n\n    if (static_cast<int>(end - ptr) < paramLen_ + 2)\n        return ParseResult::wait;\n\n    auto tail = ptr + paramLen_;\n    if (tail[0] != '\\r' || tail[1] != '\\n')\n        return ParseResult::error;\n\n    ptr = tail + 2;\n    paramLen_ = kInvalid;\n\n    return ParseResult::ok;\n}\n\nParseResult ClientProtocol::_ParseStrlen(const char*& ptr, const char* end, int& result)\n{\n    if (end - ptr < 3)\n        return ParseResult::wait;\n\n    if (*ptr != '$')\n        return ParseResult::error;\n\n    ++ ptr;\n\n    const auto ret = GetIntUntilCRLF(ptr,  end - ptr, result);\n    if (ret != ParseResult::ok)\n        -- ptr;\n\n    return ret;\n}\n\nParseResult ClientProtocol::_ParseMulti(const char*& ptr, const char* end)\n{\n    if (multi_ == kInvalid)\n    {\n        if (end - ptr < 3)\n            return ParseResult::wait;\n\n        if (*ptr != '*')\n            return ParseResult::error;\n\n        ++ ptr;\n        auto ret = GetIntUntilCRLF(ptr,  end - ptr, multi_);\n        if (ret == ParseResult::error || multi_ < -1)\n            return ParseResult::error;\n        if (ret != ParseResult::ok)\n            return ParseResult::wait;\n    }\n\n    while (nParams_ < multi_)\n    {\n        auto ret = _ParseStr(ptr, end);\n        if (ret == ParseResult::ok)\n            ++ nParams_;\n        else\n            return ret;\n    }\n\n    return ParseResult::ok;\n}\n\n#if 0\n#include <iostream>\n\nint main()\n{\n    std::string rsp = \"$-1\\r\\n\";\n    //std::string rsp = \"$3\\r\\nabc\\r\\n\";\n    //std::string rsp = \"*2\\r\\n$4\\r\\nname\\r\\n$4\\r\\nbert\\r\\n\";\n#if 0\n    ServerProtocol proto;\n#else\n    ClientProtocol proto;\n#endif\n\n    const char* start = rsp.data();\n    while (start < rsp.data() + rsp.size())\n    {\n        //const char* start = rsp.data();\n        auto ret = proto.Parse(start, start + rsp.size());\n        //auto ret = proto.ParseRequest(start, rsp.data() + rsp.size());\n        if (ret == ParseResult::error)\n            return -1;\n    }\n\n    std::cout << \"start - rsp.data() \" << start - rsp.data() << std::endl;\n    std::cout << \"result \" << proto.GetParam()  << std::endl;\n    //std::cout << \"result \" << proto.GetRawRequest()  << std::endl;\n    //for (const auto& e : proto.GetParams())\n     //   std::cout << e << std::endl;\n}\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/Protocol.h",
    "content": "#ifndef BERT_PROTOCOL_H\n#define BERT_PROTOCOL_H\n\n#include <vector>\n#include <string>\n\nenum class ParseResult : int8_t \n{ \n    ok, \n    wait, \n    error,\n};\n\nclass ServerProtocol\n{\npublic:\n    void Reset();\n    ParseResult ParseRequest(const char*& ptr, const char* end);\n\n    const std::vector<std::string>& GetParams() const & { return params_; }\n    std::vector<std::string>& GetParams()& { return params_; }\n    std::vector<std::string>&& GetParams()&& { return std::move(params_); }\n\n    const std::string& GetRawRequest() const & { return content_; }\n    std::string& GetRawRequest() & { return content_; }\n    std::string&& GetRawRequest() && { return std::move(content_); }\n\n    bool IsInitialState() const { return multi_ == -1; }\n\nprivate:\n    ParseResult _ParseMulti(const char*& ptr, const char* end, int& result);\n    ParseResult _ParseStrlist(const char*& ptr, const char* end, std::vector<std::string>& results);\n    ParseResult _ParseStr(const char*& ptr, const char* end, std::string& res);\n    ParseResult _ParseStrval(const char*& ptr, const char* end, std::string& res);\n    ParseResult _ParseStrlen(const char*& ptr, const char* end, int& result);\n\n    int multi_ = -1;\n    int paramLen_ = -1;\n    std::vector<std::string> params_;\n    int nParams_ = 0;\n    std::string content_;\n};\n\n\nenum class ResponseType\n{\n    None,\n    Fine,   // +\n    Error,  // -\n    String, // $\n    Number, // :\n    Multi,  // *\n};\n\nclass ClientProtocol\n{\npublic:\n    ClientProtocol();\n\n    void Reset();\n    ParseResult Parse(const char*& data, const char* end);\n\n    const std::string& GetContent() const& { return content_; }\n    std::string& GetContent() & { return content_; }\n    std::string&& GetContent()&& { return std::move(content_); }\n\nprivate:\n    // $3\\r\\nabc\\r\\n\n    ParseResult _ParseStr(const char*& ptr, const char* end);\n    ParseResult _ParseStrval(const char*& ptr, const char* end);\n    ParseResult _ParseStrlen(const char*& ptr, const char* end, int& result);\n\n    // *2\\r\\n$4\\r\\nname\\r\\n$4\\r\\nbert\\r\\n\n    ParseResult _ParseMulti(const char*& ptr, const char* end);\n\n    static const int kInvalid = -2;\n\n    ResponseType type_;\n    std::string content_;\n    int multi_ = kInvalid;\n    int paramLen_ = kInvalid;\n    int nParams_ = 0;\n};\n\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ProxyConfig.cc",
    "content": "#include <vector>\n#include <iostream>\n\n#include \"ProxyConfig.h\"\n#include \"ConfigParser.h\"\n\nnamespace qedis\n{\n\nstatic void EraseQuotes(std::string& str)\n{\n    // convert \"hello\" to hello\n    if (str.size() < 2)\n        return;\n\n    if (str[0] == '\"' && str[str.size() - 1] == '\"')\n    {\n        str.pop_back();\n        str.erase(str.begin());\n    }\n}\n\nconst std::string ProxyConfig::kProxyPrefixPath = \"/proxy/qedis_proxy_\";\nconst std::string ProxyConfig::kQedisSetsPath = \"/servers\";\n\nProxyConfig g_config;\n\nProxyConfig::ProxyConfig() :\n    bindAddr(\"127.0.0.1:6379\"),\n    logDir(\"log_proxy\"),\n    clusters({\"127.0.0.1:2181\"})\n{\n}\n\nbool LoadProxyConfig(const char* cfgFile, ProxyConfig& cfg)\n{\n    ConfigParser parser;\n    if (!parser.Load(cfgFile))\n        return false;\n    \n    cfg.bindAddr = parser.GetData<std::string>(\"bind\", cfg.bindAddr);\n    cfg.logDir = parser.GetData<std::string>(\"logdir\", cfg.logDir);\n\n    return true;\n}\n\n} // end namespace qedis\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ProxyConfig.h",
    "content": "#ifndef BERT_PROXYCONFIG_H\n#define BERT_PROXYCONFIG_H\n\n#include <map>\n#include <vector>\n#include <string>\n\nnamespace qedis\n{\n\nstruct ProxyConfig\n{\n    std::string bindAddr;\n    std::string logDir;\n    std::vector<std::string> clusters;\n\n    static const std::string kProxyPrefixPath;\n    static const std::string kQedisSetsPath;\n    \n    ProxyConfig();\n};\n\nextern ProxyConfig g_config;\n\nextern bool LoadProxyConfig(const char* cfgFile, ProxyConfig& cfg);\n\n}\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ProxyLog.h",
    "content": "#ifndef BERT_PROXYLOG_H\n#define BERT_PROXYLOG_H\n\n#include \"net/log/Logger.h\"\n\nextern std::shared_ptr<ananas::Logger> g_logger;\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/QedisConn.cc",
    "content": "#include <algorithm>\n#include <iostream>\n#include \"QedisConn.h\"\n\nQedisConn::QedisConn(ananas::Connection* conn) :\n    hostConn_(conn)\n{\n}\n\nstatic \nstd::string BuildRequest(const std::vector<std::string>& params)\n{\n    assert (!params.empty());\n\n    std::string req;\n    for (const auto& e : params)\n    {\n        req += e + \" \";\n    }\n\n    req.pop_back();\n    req += \"\\r\\n\";\n\n    return req;\n}\n\nananas::Future<std::string>\nQedisConn::ForwardRequest(const std::vector<std::string>& params)\n{\n    std::string buf = BuildRequest(params);\n    return ForwardRequest(buf);\n}\n\nananas::Future<std::string>\nQedisConn::ForwardRequest(const std::string& rawReq)\n{\n    hostConn_->SendPacket(rawReq.data(), rawReq.size());\n\n    QedisConn::Request req;\n\n    auto fut = req.promise.GetFuture();\n    pending_.push(std::move(req));\n\n    return fut;\n}\n    \nananas::PacketLen_t QedisConn::OnRecv(ananas::Connection* conn, const char* data, ananas::PacketLen_t len)\n{\n    const char* ptr = data;\n    auto parseRet = proto_.Parse(ptr, ptr + len);\n    if (parseRet == ParseResult::error)\n    {\n        conn->ActiveClose();\n        return 0;\n    }\n    else if (parseRet != ParseResult::ok) \n    {\n        // wait\n        return static_cast<ananas::PacketLen_t>(ptr - data); \n    }\n\n    assert (parseRet == ParseResult::ok);\n        \n    auto& req = pending_.front();\n    req.promise.SetValue(proto_.GetContent());\n    pending_.pop();\n\n    proto_.Reset();\n\n    return static_cast<ananas::PacketLen_t>(ptr - data);\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/QedisConn.h",
    "content": "#ifndef BERT_QEDISCONN_H\n#define BERT_QEDISCONN_H\n\n#include <queue>\n#include <string>\n#include <vector>\n#include \"net/Connection.h\"\n#include \"future/Future.h\"\n#include \"Protocol.h\"\n\n// Build redis request from multiple strings, use inline protocol \ntemplate <typename... Args>\nstd::string BuildQedisRequest(Args&& ...);\n\ntemplate <typename S>\nstd::string BuildQedisRequest(S&& s)\n{\n    return std::string(std::forward<S>(s)) + \"\\r\\n\";\n}\n\ntemplate <typename H, typename... T>\nstd::string BuildQedisRequest(H&& head, T&&... tails)\n{\n    std::string h(std::forward<H>(head));\n    return h + \" \" + BuildQedisRequest(std::forward<T>(tails)...);\n}\n\nclass QedisConn final\n{\npublic:\n    explicit\n    QedisConn(ananas::Connection* conn);\n\n    ananas::Future<std::string> \n    ForwardRequest(const std::vector<std::string>& params);\n\n    ananas::Future<std::string>\n    ForwardRequest(const std::string& rawRequest);\n\n    ananas::PacketLen_t OnRecv(ananas::Connection* conn, const char* data, ananas::PacketLen_t len);\nprivate:\n    ananas::Connection* hostConn_;\n\n    struct Request\n    {\n        //std::vector<std::string> request;\n        ananas::Promise<std::string> promise;\n\n        Request() = default;\n\n        Request(Request const& ) = delete;\n        void operator= (Request const& ) = delete;\n\n        Request(Request&& ) = default;\n        Request& operator= (Request&& ) = default;\n    };\n\n    std::queue<Request> pending_;\n\n    ClientProtocol proto_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/QedisManager.cc",
    "content": "#include \"QedisManager.h\"\n#include \"QedisConn.h\"\n#include \"ProxyLog.h\"\n\n#include \"net/EventLoop.h\"\n#include \"net/Connection.h\"\n#include \"net/Socket.h\"\n\nQedisManager& QedisManager::Instance()\n{\n    static QedisManager mgr;\n    return mgr;\n}\n\nQedisManager::QedisManager() :\n    loop_(nullptr)\n{\n}\n\nvoid QedisManager::SetEventLoop(ananas::EventLoop* loop)\n{\n    assert (!loop_);\n    loop_ = loop;\n}\n\nananas::Future<QedisConn* > QedisManager::Connect(const std::string& addr)\n{\n    ConnectPromise promise;\n    auto fut = promise.GetFuture();\n    auto& promiseList = pending_[addr];\n    promiseList.emplace_back(std::move(promise));\n\n    if (promiseList.size() == 1)\n    {\n        bool succ = loop_->Connect(ananas::SocketAddr(addr),\n                                   std::bind(&QedisManager::OnNewConnection, this, std::placeholders::_1),\n                                   std::bind(&QedisManager::OnConnFail, this, std::placeholders::_1, std::placeholders::_2), \n                                   ananas::DurationMs(1000));\n        if (!succ)\n        {\n            pending_.erase(addr);\n            return ananas::MakeExceptionFuture<QedisConn* >(std::runtime_error(\"connect \" + addr + \" failed\"));\n        }\n    }\n\n    return fut;\n}\n\nvoid QedisManager::OnNewConnection(ananas::Connection* conn)\n{\n    INF(g_logger) << \"QedisManager::OnNewConnection \" << conn->Identifier();\n\n    auto ctx = std::make_shared<QedisConn>(conn);\n\n    conn->SetUserData(ctx);\n    conn->SetMinPacketSize(4);\n    conn->SetOnMessage(std::bind(&QedisConn::OnRecv,\n                                 ctx.get(),\n                                 std::placeholders::_1,\n                                 std::placeholders::_2,\n                                 std::placeholders::_3));\n    conn->SetOnConnect(std::bind(&QedisManager::OnConnect, this, std::placeholders::_1));\n    conn->SetOnDisconnect(std::bind(&QedisManager::OnDisconnect, this, std::placeholders::_1));\n}\n\nvoid QedisManager::OnConnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"QedisManager::OnConnect \" << conn->Peer().ToString();\n    auto ctx = conn->GetUserData<QedisConn>();\n\n    auto req = pending_.find(conn->Peer().ToString());\n    assert (req != pending_.end());\n\n    bool succ = connMap_.insert({conn->Peer().ToString(), conn}).second;\n    assert (succ);\n\n    for (auto& prom : req->second)\n        prom.SetValue(ctx.get());\n\n    pending_.erase(req);\n}\n\nvoid QedisManager::OnDisconnect(ananas::Connection* conn)\n{\n    INF(g_logger) << \"QedisManager::OnDisconnect \" << conn->Identifier();\n    size_t n = connMap_.erase(conn->Peer().ToString());\n    assert (n > 0);\n}\n\nvoid QedisManager::OnConnFail(ananas::EventLoop* loop, const ananas::SocketAddr& peer)\n{\n    INF(g_logger) << \"QedisManager::OnConnFail \" << peer.ToString();\n\n    auto req = pending_.find(peer.ToString());\n    if (req != pending_.end())\n    {\n        for (auto& prom : req->second)\n            prom.SetException(std::make_exception_ptr(std::runtime_error(\"Failed connect to \" + peer.ToString())));\n\n        pending_.erase(req);\n    }\n}\n\nananas::Future<QedisConn* > QedisManager::GetConnection(const std::string& peer)\n{\n    auto it = connMap_.find(peer);\n    if (it != connMap_.end())\n        return ananas::MakeReadyFuture<QedisConn* >(it->second->GetUserData<QedisConn>().get());\n\n    return this->Connect(peer);\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/QedisManager.h",
    "content": "#ifndef BERT_QEDISMANAGER_H\n#define BERT_QEDISMANAGER_H\n\n#include <unordered_map>\n#include <vector>\n#include <string>\n\n#include \"future/Future.h\"\n\n\nclass QedisConn;\n\nnamespace ananas\n{\n    class EventLoop;\n    class Connection;\n    struct SocketAddr;\n}\n\nclass QedisManager\n{\npublic:\n    static QedisManager& Instance();\n\n    QedisManager();\n\n    void SetEventLoop(ananas::EventLoop* loop);\n\n    ananas::Future<QedisConn* > Connect(const std::string& addr);\n\n    void OnNewConnection(ananas::Connection* conn);\n    void OnConnect(ananas::Connection* conn);\n    void OnDisconnect(ananas::Connection* conn);\n    void OnConnFail(ananas::EventLoop* loop, const ananas::SocketAddr& peer);\n\n    ananas::Future<QedisConn* > GetConnection(const std::string& peer);\n\nprivate:\n    ananas::EventLoop* loop_;\n    std::unordered_map<std::string, ananas::Connection* > connMap_;\n\n    struct Request\n    {\n        std::string peer;\n        ananas::Promise<QedisConn* > promise;\n\n        Request()\n        {\n        }\n\n        Request(Request const& ) = delete;\n        void operator= (Request const& ) = delete;\n\n        Request(Request&& ) = default;\n        Request& operator= (Request&& ) = default;\n    };\n\n    using ConnectPromise = ananas::Promise<QedisConn* >;\n    std::unordered_map<std::string, std::vector<ConnectPromise> > pending_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ZookeeperConn.cc",
    "content": "#include <iostream>\n#include <sys/time.h>\n\n#include \"net/Connection.h\"\n#include \"net/EventLoop.h\"\n#include \"util/Util.h\"\n\n#include \"ZookeeperConn.h\"\n#include \"ProxyConfig.h\"\n#include \"ClusterManager.h\"\n\nZookeeperConn::ZookeeperConn(ananas::Connection* c) :\n    conn_(c)\n{\n    ctx_.reset(new qedis::ZookeeperContext(c));\n}\n    \nZookeeperConn::~ZookeeperConn()\n{\n}\n\nbool ZookeeperConn::OnData(const char*& data, size_t len)\n{\n    return ctx_->ParseMessage(data, len);\n}\n\nananas::Try<qedis::ZookeeperContext* >\nZookeeperConn::_ProcessHandshake(const ZkResponse& rsp) \n{\n    if (ctx_->ProcessHandshake(rsp))\n    {\n        std::cout << \"DoHandshake succ\\n\";\n        return ananas::Try<qedis::ZookeeperContext* >(ctx_.get());\n    }\n    else\n    {\n        conn_->ActiveClose();\n        auto exptr = std::make_exception_ptr(std::runtime_error(\"ProcessHandshake failed\"));\n        return ananas::Try<qedis::ZookeeperContext* >(exptr);\n    }\n}\n\nananas::Future<std::vector<ananas::Try<ZkResponse>>>\nZookeeperConn::_RegisterAndGetServers(ananas::Try<qedis::ZookeeperContext* >&& tctx)\n{\n    std::vector<ananas::Future<ZkResponse> > futures;\n    try\n    {\n        qedis::ZookeeperContext* ctx = tctx;\n                    \n        if (!ctx->IsResumed())\n        {\n            const std::string& data = qedis::g_config.bindAddr;\n            const std::string& path = qedis::ProxyConfig::kProxyPrefixPath;\n            auto fut = ctx->CreateNode(true, true, &data, &path);\n            futures.emplace_back(std::move(fut));\n        }\n\n        // 3. get qedis server's set and watch the set's list\n        auto fut = ctx->GetChildren2(qedis::ProxyConfig::kQedisSetsPath, true);\n        futures.emplace_back(std::move(fut));\n    }\n    catch (const std::exception& e)\n    {\n        std::cout << \"OnConnect exception \" << e.what() << std::endl;\n    }\n\n    return ananas::WhenAll(std::begin(futures), std::end(futures));\n}\n\nananas::Future<std::vector<ananas::Try<ZkResponse>>>\nZookeeperConn::_GetShardingInfo(const std::vector<ananas::Try<ZkResponse>>& rsps)\n{\n    std::vector<ananas::Future<ZkResponse> > futures;\n    if (!rsps.empty())\n    {\n        const ZkResponse& sets = rsps.back();\n        ctx_->ProcessGetChildren2(sets);\n\n        auto grsp = AnyCast<ChildrenRsp>(sets);\n        for (const auto& child : grsp->children)\n        {\n            std::cout << \"Try get data \" << qedis::ProxyConfig::kQedisSetsPath + \"/\" + child << std::endl;\n            auto fut = ctx_->GetData(qedis::ProxyConfig::kQedisSetsPath + \"/\" + child, true);\n            futures.emplace_back(std::move(fut));\n        }\n    }\n\n    return ananas::WhenAll(std::begin(futures), std::end(futures));\n}\n\n// /servers/set-1\nstatic int GetSetID(const std::string& path)\n{\n    auto pos = path.find_last_of('-');\n    if (pos == std::string::npos)\n        return -1;\n\n    std::string number(path.substr(pos + 1));\n    return std::stoi(number);\n}\n\nbool ZookeeperConn::_ProcessShardingInfo(const std::vector<ananas::Try<ZkResponse>>& vrsp)\n{\n    for (const auto& rsp : vrsp)\n    {\n        ctx_->ProcessGetData(rsp);\n\n        //ZkResponse rsp2 = rsp;\n        auto drsp = AnyCast<DataRsp>(rsp.Value());\n        \n        // /servers/set-1\n        int setid = GetSetID(drsp->path);\n        if (setid < 0)\n        {\n            std::cout << \"Wrong setid \" << setid << std::endl;\n            return false;\n        }\n            \n        std::cout << \"Path = \" << drsp->path << \", setid \" << setid << std::endl;\n        std::cout << \"Value = \" << drsp->data << std::endl;\n        \n        std::vector<std::string> shardings = ananas::SplitString(drsp->data, ',');\n        ClusterManager::Instance().AddShardingInfo(setid, shardings);\n    }\n\n    return true;\n}\n    \nananas::Future<std::vector<ananas::Try<ZkResponse>>>\nZookeeperConn::_GetServers(const std::vector<ananas::Try<ZkResponse>>& vrsp)\n{\n    std::vector<ananas::Future<ZkResponse> > futures;\n\n    for (const auto& rsp : vrsp)\n    {\n        ctx_->ProcessGetData(rsp);\n\n        auto drsp = AnyCast<DataRsp>(rsp.Value());\n        \n        // /servers/set-1\n        auto fut = ctx_->GetChildren2(drsp->path, true);\n        futures.emplace_back(std::move(fut));\n    }\n\n    return ananas::WhenAll(std::begin(futures), std::end(futures));\n}\n\nstatic std::string GetNodeAddr(const std::string& path)\n{\n    // /servers/set-{setid}/qedis(ip:port)-xxxseq\n    auto start = path.find_first_of('(');\n    auto end = path.find_first_of(')');\n    if (start == std::string::npos ||\n        end == std::string::npos)\n        return std::string();\n\n    return path.substr(start + 1, end - start - 1);\n}\n\n\nbool ZookeeperConn::_ProcessServerInfo(const std::vector<ananas::Try<ZkResponse>>& vrsp)\n{\n    for (const auto& rsp : vrsp)\n    {\n        ctx_->ProcessGetChildren2(rsp);\n\n        auto crsp = AnyCast<ChildrenRsp>(rsp.Value());\n            \n        std::cout << \"Parent& \" << crsp->parent << std::endl;\n\n        int setid = GetSetID(crsp->parent);\n        for (const auto& child : crsp->children)\n        {\n            // qedis(127.0.0.1:6379)-0000000215\n            std::string data = GetNodeAddr(child);\n            std::cout << \"child \" << data << std::endl;\n            ClusterManager::Instance().AddServerInfo(setid, data);\n        }\n    }\n\n    return true;\n}\n\nvoid ZookeeperConn::_InitPingTimer() \n{\n    auto doPing = [conn = conn_, ctx = ctx_.get()]()\n                  {\n                      timeval now;\n                      ::gettimeofday(&now, nullptr);\n                      long lastPing = now.tv_sec * 1000;\n                      lastPing += now.tv_usec / 1000;\n                      auto processPing = std::bind(&qedis::ZookeeperContext::ProcessPing, ctx, lastPing, std::placeholders::_1);\n                      ctx->Ping().Then(processPing);\n                  };\n\n    pingId_ = conn_->GetLoop()->ScheduleAfter<ananas::kForever>(std::chrono::seconds(3), doPing);\n}\n    \nvoid ZookeeperConn::OnConnect()\n{\n    ctx_->DoHandshake()\n        .Then([me = this](const ZkResponse& rsp) mutable {\n            // 1. Handshake with zookeeper\n            return me->_ProcessHandshake(rsp);\n        })\n        .Then([me = this](ananas::Try<qedis::ZookeeperContext* >&& tctx) mutable {\n            // 2. Register me and get redis sets' info\n            return me->_RegisterAndGetServers(std::move(tctx));\n        })\n        .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& rsps) mutable {\n            // 3. Get the qedis sets's sharding info\n            return me->_GetShardingInfo(rsps);\n        })\n        .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& vrsp) mutable {\n            // 4. store qedis sets' sharding info\n            if (!me->_ProcessShardingInfo(vrsp)) {\n                using InnerType = std::vector<ananas::Try<ZkResponse>>;\n                auto exp = std::runtime_error(\"ProcessShardingInfo failed\");\n                return ananas::MakeExceptionFuture<InnerType>(exp);\n            }\n\n            // 5. get qedis server's list and watch the qedis server list\n            return me->_GetServers(vrsp);\n        })\n        .Then([me = this](const std::vector<ananas::Try<ZkResponse> >& vrsp) mutable {\n            // 6. store qedis servers' info\n            return me->_ProcessServerInfo(vrsp);\n        })\n        .Then([me = this](bool succ) {\n            // 7. Init ping timer\n            if (succ) {\n                me->_InitPingTimer();\n            }\n        })\n        .OnTimeout(std::chrono::seconds(5), []() {\n                std::cout << \"OnTimeout handshake\\n\";\n                ananas::EventLoop::ExitApplication();\n            }, conn_->GetLoop()\n        );\n}\n\nvoid ZookeeperConn::OnDisconnect()\n{\n    if (pingId_)\n        conn_->GetLoop()->Cancel(pingId_);\n}\n\n"
  },
  {
    "path": "cluster/qedis_proxy/ZookeeperConn.h",
    "content": "#ifndef BERT_ZOOKEEPERCONN_H\n#define BERT_ZOOKEEPERCONN_H\n\n#include <memory>\n#include \"ClusterConn.h\"\n#include \"zookeeper/ZookeeperContext.h\"\n\nnamespace ananas\n{\nclass Connection;\n}\n\n// 当连接zk成功时，先发起握手，再获取QEDIS节点列表，然后再监听集群客户端\n//\n// 对于集群客户端，先连接zk，成功后，同时注册自己和获取PROXY节点列表，然后就可以发起请求\n// PROXY节点无状态，可以随意负载均衡的请求\nclass ZookeeperConn final : public qedis::ClusterConn\n{\npublic:\n    explicit\n    ZookeeperConn(ananas::Connection* c);\n\n    virtual ~ZookeeperConn();\n\npublic:\n    bool OnData(const char*& data, size_t len) override final;\n    void OnConnect() override final;\n    void OnDisconnect() override final;\n\nprivate:\n    ananas::Try<qedis::ZookeeperContext* >\n        _ProcessHandshake(const ZkResponse& );\n\n    ananas::Future<std::vector<ananas::Try<ZkResponse>>>\n        _RegisterAndGetServers(ananas::Try<qedis::ZookeeperContext* >&& );\n\n    ananas::Future<std::vector<ananas::Try<ZkResponse>>>\n        _GetShardingInfo(const std::vector<ananas::Try<ZkResponse>>& );\n\n    bool _ProcessShardingInfo(const std::vector<ananas::Try<ZkResponse>>& );\n\n    ananas::Future<std::vector<ananas::Try<ZkResponse>>>\n        _GetServers(const std::vector<ananas::Try<ZkResponse>>& );\n\n    bool _ProcessServerInfo(const std::vector<ananas::Try<ZkResponse>>& );\n    void _InitPingTimer();\n\n    ananas::Connection* conn_;\n    std::unique_ptr<qedis::ZookeeperContext> ctx_;\n\n    ananas::TimerId pingId_;\n};\n\n#endif\n\n"
  },
  {
    "path": "cluster/qedis_proxy/main.cc",
    "content": "#include <iostream>\n\n#include \"ProxyLog.h\"\n#include \"ProxyConfig.h\"\n#include \"ananas/net/EventLoop.h\"\n#include \"ClusterManager.h\"\n#include \"ClientManager.h\"\n#include \"QedisManager.h\"\n#include \"Command.h\"\n\nstd::shared_ptr<ananas::Logger> g_logger;\n\nint main(int ac, char* av[])\n{\n    using namespace qedis;\n\n    if (ac > 1)\n    {\n        if (!LoadProxyConfig(av[1], g_config))\n        {\n            std::cout << \"LoadProxyConfig \" << av[1] << \" failed!\\n\";\n            return -1;\n        }\n    }\n\n    ananas::LogManager::Instance().Start();\n    g_logger = ananas::LogManager::Instance().CreateLog(logERROR, logALL, g_config.logDir.data());\n\n    CommandTable::Init();\n\n    for (const auto& addr : g_config.clusters)\n        ClusterManager::Instance().AddClusterAddr(addr);\n\n    ananas::EventLoop loop;\n    ClusterManager::Instance().SetEventLoop(&loop);\n    if (!ClusterManager::Instance().Connect())\n        ananas::EventLoop::ExitApplication();\n\n    ClientManager::Instance().SetEventLoop(&loop);\n    if (!ClientManager::Instance().Listen(g_config.bindAddr))\n        ananas::EventLoop::ExitApplication();\n\n    QedisManager::Instance().SetEventLoop(&loop);\n\n    loop.Run();\n\n    return 0;\n}\n\n"
  },
  {
    "path": "qedis.conf",
    "content": "# Qedis configuration file example\n\n# By default Qedis does not run as a daemon. Use 'yes' if you need it.\ndaemonize no\n\n# Write your extensions use module, the modules will load at startup\n# or your can use module load command to load module at runtime.\n# On linux, you should change suffix from \"dylib\" to \"so\"\nloadmodule libqedismodule.dylib\nloadmodule libnotexist.dylib # a lib not exist, load raise error \n\n# Accept connections on the specified port, default is 6379.\n# port 0 is not permitted.\nport 6379\n\n# If you want you can bind a single interface, if the bind option is not\n# specified all the interfaces will listen for incoming connections.\n#\n# bind 127.0.0.1\n\n\n# Close the connection after a client is idle for N seconds (0 to disable)\ntimeout 0\n\n# Specify the server verbosity level.\n# This can be one of:\n# debug (a lot of information, useful for development/testing)\n# verbose (many rarely useful info, but not a mess like the debug level)\n# notice (moderately verbose, what you want in production probably)\n# warning (only very important / critical messages are logged)\nloglevel warning\n\n# Specify the log file name. Also 'stdout' 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 stdout\n\n# Set the number of databases. The default database is DB 0, you can select\n# a different one on a per-connection basis using SELECT <dbid> where\n# dbid is a number between 0 and 'databases'-1\ndatabases 16\n\n################################ SNAPSHOTTING  #################################\n#\n# Save the DB on disk:\n#\n#   save <seconds> <changes>\n#\n#   Will save the DB if both the given number of seconds and the given\n#   number of write operations against the DB occurred.\n#\n#   In the example below the behaviour will be to save:\n#   after 900 sec (15 min) if at least 1 key changed\n#   after 300 sec (5 min) if at least 10 keys changed\n#   after 60 sec if at least 10000 keys changed\n#\n#   Note: you can disable saving at all commenting all the \"save\" lines.\n#\n#   It is also possible to remove all the previously configured save\n#   points by adding a save directive with a single empty string argument\n#   like in the following example:\n#\nsave \"\"\n\n#save 900 1\n#save 300 10\n#save 60000 1000000\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 an hard way) that data is not persisting\n# on disk properly, otherwise chances are that no one will notice and some\n# distater 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 usually even if there are problems with disk,\n# permissions, and so forth.\nstop-writes-on-bgsave-error yes # not support\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 # Qedis always use compression for rdb file\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 # Qedis always check sum for rdb file\n\n# The filename where to dump the DB\ndbfilename dump.rdb\n\n# The working directory.\n#\n# The DB will be written inside this directory, with the filename specified\n# above using the 'dbfilename' configuration directive.\n# \n# The Append Only File will also be created inside this directory.\n# \n# Note that you must specify a directory here, not a file name.\ndir ./\n\n################################# REPLICATION #################################\n\n# Master-Slave replication. Use slaveof to make a Redis instance a copy of\n# another Redis server. Note that the configuration is local to the slave\n# so for example it is possible to configure the slave to save the DB with a\n# different interval, or to listen to another port, and so on.\n#\n# slaveof <masterip> <masterport>\n# slaveof 127.0.0.1 6379\n\n# If the master is password protected (using the \"requirepass\" configuration\n# directive below) it is possible to tell the slave to authenticate before\n# starting the replication synchronization process, otherwise the master will\n# refuse the slave request.\n#\n# masterauth foobar\n\n# When a slave loses its connection with the master, or when the replication\n# is still in progress, the slave can act in two different ways:\n#\n# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will\n#    still reply to client requests, possibly with out of date data, or the\n#    data set may just be empty if this is the first synchronization.\n#\n# 2) if slave-serve-stale-data is set to 'no' the slave will reply with\n#    an error \"SYNC with master in progress\" to all the kind of commands\n#    but to INFO and SLAVEOF.\n#\n# slave-serve-stale-data yes # not support yet\n\n# You can configure a slave instance to accept writes or not. Writing against\n# a slave instance may be useful to store some ephemeral data (because data\n# written on a slave will be easily deleted after resync with the master) but\n# may also cause problems if clients are writing to it because of a\n# misconfiguration.\n#\n# Since Redis 2.6 by default slaves are read-only.\n#\n# Note: read only slaves are not designed to be exposed to untrusted clients\n# on the internet. It's just a protection layer against misuse of the instance.\n# Still a read only slave exports by default all the administrative commands\n# such as CONFIG, DEBUG, and so forth. To a limited extend you can improve\n# security of read only slaves using 'rename-command' to shadow all the\n# administrative / dangerous commands.\nslave-read-only yes # Qedis always set slave read only\n\n# Slaves send PINGs to server in a predefined interval. It's possible to change\n# this interval with the repl_ping_slave_period option. The default value is 10\n# seconds.\n#\n# repl-ping-slave-period 10\n\n# The following option sets a timeout for both Bulk transfer I/O timeout and\n# master data or ping response timeout. The default value is 60 seconds.\n#\n# It is important to make sure that this value is greater than the value\n# specified for repl-ping-slave-period otherwise a timeout will be detected\n# every time there is low traffic between the master and the slave.\n#\n# repl-timeout 60\n\n# The slave priority is an integer number published by Redis in the INFO output.\n# It is used by Redis Sentinel in order to select a slave to promote into a\n# master if the master is no longer working correctly.\n#\n# A slave with a low priority number is considered better for promotion, so\n# for instance if there are three slaves with priority 10, 100, 25 Sentinel will\n# pick the one wtih priority 10, that is the lowest.\n#\n# However a special priority of 0 marks the slave as not able to perform the\n# role of master, so a slave with priority of 0 will never be selected by\n# Redis Sentinel for promotion.\n#\n# By default the priority is 100.\nslave-priority 100 # not support yet\n\n################################## SECURITY ###################################\n\n# Require clients to issue AUTH <PASSWORD> before processing any other\n# commands.  This might be useful in environments in which you do not trust\n# others with access to the host running redis-server.\n#\n# This should stay commented out for backward compatibility and because most\n# people do not need auth (e.g. they run their own servers).\n# \n# Warning: since Redis is pretty fast an outside user can try up to\n# 150k passwords per second against a good box. This means that you should\n# use a very strong password otherwise it will be very easy to break.\n#\n#requirepass foobar\n\n# Command renaming.\n#\n# It is possible to change the name of dangerous commands in a shared\n# environment. For instance the CONFIG command may be renamed into something\n# hard to guess so that it will still be available for internal-use tools\n# but not available for general clients.\n#\n# Example:\n#\n# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52\n#\n# It is also possible to completely kill a command by renaming it into\n# an empty string:\n#\n# rename-command CONFIG \"\"\n#\n# Please note that changing the name of commands that are logged into the\n# AOF file or transmitted to slaves may cause problems.\n\n################################### LIMITS ####################################\n\n# Set the max number of connected clients at the same time. By default\n# this limit is set to 10000 clients, however if the Redis server is not\n# able to configure the process file limit to allow for the specified limit\n# the max number of allowed clients is set to the current file limit\n# minus 32 (as Redis reserves a few file descriptors for internal uses).\n#\n# Once the limit is reached Redis will close all the new connections sending\n# an error 'max number of clients reached'.\n#\n# maxclients 10000\n\n# Don't use more memory than the specified amount of bytes.\n# When the memory limit is reached Redis will try to remove keys\n# accordingly to the eviction policy selected (see maxmemmory-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#\nmaxmemory 999999999999\n#\n# MAXMEMORY POLICY: how Qedis will select what to remove when maxmemory\n# is reached. You can select among five behaviors:\n# \n# allkeys-lru -> remove any key accordingly to the LRU algorithm\n# noeviction -> don't expire at all, just return an error on write operations\n# The default is:\n#\nmaxmemory-policy noeviction\n\n# LRU and minimal TTL algorithms are not precise algorithms but approximated\n# algorithms (in order to save memory), so you can select as well the sample\n# size to check. For instance for default Qedis will check 5 keys and\n# pick the one that was used less recently, you can change the sample size\n# using the following configuration directive.\n#\nmaxmemory-samples 5\n\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# appendfilename appendonly.aof\n\n# The fsync() call tells the Operating System to actually write data on disk\n# instead to wait for more data in the output buffer. Some OS will really flush \n# data on disk, some other OS will just try to do it ASAP.\n#\n# Redis supports three different modes:\n#\n# no: don't fsync, just let the OS flush the data when it wants. Faster.\n# always: fsync after every write to the append only log . Slow, Safest.\n# everysec: fsync only one time every second. Compromise.\n#\n# The default is \"everysec\", as that's usually the right compromise between\n# speed and data safety. It's up to you to understand if you can relax this to\n# \"no\" that will let the operating system flush the output buffer when\n# it wants, for better performances (but if you can live with the idea of\n# some data loss consider the default persistence mode that's snapshotting),\n# or on the contrary, use \"always\" that's very slow but a bit safer than\n# everysec.\n#\n# More details please check the following article:\n# http://antirez.com/post/redis-persistence-demystified.html\n#\n# If unsure, use \"everysec\".\n\n# appendfsync always\n#appendfsync everysec\nappendfsync no # Qedis use mmap and msync\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.\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\n#auto-aof-rewrite-percentage 100\n#auto-aof-rewrite-min-size 64mb\n\n################################ LUA SCRIPTING  ###############################\n\n# Max execution time of a Lua script in milliseconds.\n#\n# If the maximum execution time is reached Redis will log that a script is\n# still in execution after the maximum allowed time and will start to\n# reply to queries with an error.\n#\n# When a long running script exceed the maximum execution time only the\n# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be\n# used to stop a script that did not yet called write commands. The second\n# is the only way to shut down the server in the case a write commands was\n# already issue by the script but the user don't want to wait for the natural\n# termination of the script.\n#\n# Set it to 0 or a negative value for unlimited execution without warnings.\n#lua-time-limit 5000\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############################### ADVANCED CONFIG ###############################\n\n# Redis calls an internal function to perform many background tasks, like\n# closing connections of clients in timeot, purging expired keys that are\n# never requested, and so forth.\n#\n# Not all tasks are perforemd with the same frequency, but Redis checks for\n# tasks to perform accordingly to the specified \"hz\" value.\n#\n# By default \"hz\" is set to 10. Raising the value will use more CPU when\n# Redis is idle, but at the same time will make Redis more responsive when\n# there are many keys expiring at the same time, and timeouts may be\n# handled with more precision.\n#\n# The range is between 1 and 500, however a value over 100 is usually not\n# a good idea. Most users should use the default of 10 and raise this up to\n# 100 only in environments where very low latency is required.\nhz 10\n############################### BACKENDS CONFIG ###############################\n# Qedis is a in memory database, though it has aof and rdb for dump data to disk, it\n# is very limited. Try use leveldb for real storage, qedis as cache. The cache algorithm\n# is like linux page cache, please google or read your favorite linux book\n# 0 is default, no backend\n# 1 is leveldb, currently only support leveldb\nbackend 0\nbackendpath dump\n# the frequency of dump to backend per second\nbackendhz 10\n\n############################### CLUSTER CONFIG ###############################\n#\ncluster off\n\n# the cluster center, may be zookeeper or etcd \nclustercenters 127.0.0.1:52181;127.0.0.1:2181\n\n# which set this instance belong to\n# may be many instances in the same setid, but only one can be master, others are slaves\nsetid 1\n\n"
  }
]