Repository: loveyacper/Qedis Branch: master Commit: a68b8261737b Files: 217 Total size: 1.1 MB Directory structure: gitextract_0j0gzhnc/ ├── .gitignore ├── .travis.yml ├── CMakeCommon ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── Modules/ │ ├── CMakeLists.txt │ ├── QHashModule.cc │ ├── QHashModule.h │ ├── QListModule.cc │ ├── QListModule.h │ ├── QModuleInit.cc │ ├── QModuleInit.h │ ├── QSetModule.cc │ └── QSetModule.h ├── QBase/ │ ├── AsyncBuffer.cc │ ├── AsyncBuffer.h │ ├── Buffer.h │ ├── CMakeLists.txt │ ├── ClientSocket.cc │ ├── ClientSocket.h │ ├── ConfigParser.cc │ ├── ConfigParser.h │ ├── Delegate.h │ ├── EPoller.cc │ ├── EPoller.h │ ├── Kqueue.cc │ ├── Kqueue.h │ ├── ListenSocket.cc │ ├── ListenSocket.h │ ├── Log/ │ │ ├── Logger.cc │ │ ├── Logger.h │ │ ├── MemoryFile.cc │ │ └── MemoryFile.h │ ├── NetThreadPool.cc │ ├── NetThreadPool.h │ ├── Poller.h │ ├── README.md │ ├── Server.cc │ ├── Server.h │ ├── Socket.cc │ ├── Socket.h │ ├── StreamSocket.cc │ ├── StreamSocket.h │ ├── TaskManager.cc │ ├── TaskManager.h │ ├── Threads/ │ │ ├── ThreadPool.cc │ │ └── ThreadPool.h │ ├── Timer.cc │ ├── Timer.h │ ├── UnboundedBuffer.cc │ ├── UnboundedBuffer.h │ └── lzf/ │ ├── lzf.h │ ├── lzfP.h │ ├── lzf_c.c │ └── lzf_d.c ├── QSentinel/ │ ├── CMakeLists.txt │ ├── QClusterClient.cc │ ├── QClusterClient.h │ ├── QClusterInterface.h │ ├── README.md │ └── zookeeper/ │ ├── ZookeeperConn.cc │ ├── ZookeeperConn.h │ ├── proto.h │ ├── recordio.c │ ├── recordio.h │ ├── zookeeper.jute.c │ ├── zookeeper.jute.h │ └── zookeeper_version.h ├── Qedis.xcodeproj/ │ └── project.pbxproj ├── QedisCore/ │ ├── CMakeLists.txt │ ├── QAOF.cc │ ├── QAOF.h │ ├── QClient.cc │ ├── QClient.h │ ├── QCommand.cc │ ├── QCommand.h │ ├── QCommon.cc │ ├── QCommon.h │ ├── QConfig.cc │ ├── QConfig.h │ ├── QDB.cc │ ├── QDB.h │ ├── QDumpInterface.h │ ├── QGlobRegex.cc │ ├── QGlobRegex.h │ ├── QHash.cc │ ├── QHash.h │ ├── QHelper.cc │ ├── QHelper.h │ ├── QKeyCommand.cc │ ├── QLeveldb.cc │ ├── QLeveldb.h │ ├── QList.cc │ ├── QList.h │ ├── QMigration.cc │ ├── QMigration.h │ ├── QModule.cc │ ├── QModule.h │ ├── QMulti.cc │ ├── QMulti.h │ ├── QProtoParser.cc │ ├── QProtoParser.h │ ├── QPubsub.cc │ ├── QPubsub.h │ ├── QReplication.cc │ ├── QReplication.h │ ├── QServerCommand.cc │ ├── QSet.cc │ ├── QSet.h │ ├── QSlaveClient.cc │ ├── QSlaveClient.h │ ├── QSlowLog.cc │ ├── QSlowLog.h │ ├── QSortedSet.cc │ ├── QSortedSet.h │ ├── QStore.cc │ ├── QStore.h │ ├── QString.cc │ ├── QString.h │ ├── crc64.c │ ├── redisIntset.c │ ├── redisIntset.h │ ├── redisZipList.c │ └── redisZipList.h ├── QedisSvr/ │ ├── CMakeLists.txt │ ├── Qedis.cc │ ├── Qedis.h │ └── QedisLogo.h ├── README.md ├── README.zh.md ├── UnitTest/ │ ├── CMakeLists.txt │ ├── QGlobRegex_unittest.cc │ ├── UnitTest.cc │ └── UnitTest.h ├── cluster/ │ ├── .gitignore │ ├── CMakeCommon │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── ananas/ │ │ ├── CMakeLists.txt │ │ ├── future/ │ │ │ ├── Future.h │ │ │ ├── Helper.h │ │ │ └── Try.h │ │ ├── net/ │ │ │ ├── Acceptor.cc │ │ │ ├── Acceptor.h │ │ │ ├── AnanasDebug.cc │ │ │ ├── AnanasDebug.h │ │ │ ├── AnanasLogo.h │ │ │ ├── Buffer.cc │ │ │ ├── Buffer.h │ │ │ ├── CMakeLists.txt │ │ │ ├── Connection.cc │ │ │ ├── Connection.h │ │ │ ├── Connector.cc │ │ │ ├── Connector.h │ │ │ ├── DatagramSocket.cc │ │ │ ├── DatagramSocket.h │ │ │ ├── Epoller.cc │ │ │ ├── Epoller.h │ │ │ ├── EventLoop.cc │ │ │ ├── EventLoop.h │ │ │ ├── Kqueue.cc │ │ │ ├── Kqueue.h │ │ │ ├── Poller.h │ │ │ ├── Socket.cc │ │ │ ├── Socket.h │ │ │ ├── ThreadPool.cc │ │ │ ├── ThreadPool.h │ │ │ ├── TimeUtil.cc │ │ │ ├── TimeUtil.h │ │ │ ├── Timer.cc │ │ │ ├── Timer.h │ │ │ ├── Typedefs.h │ │ │ └── log/ │ │ │ ├── Logger.cc │ │ │ ├── Logger.h │ │ │ ├── MmapFile.cc │ │ │ ├── MmapFile.h │ │ │ └── README.md │ │ └── util/ │ │ ├── Delegate.h │ │ ├── Scheduler.h │ │ └── Util.h │ ├── cluster_conn/ │ │ ├── CMakeLists.txt │ │ ├── ClusterConn.h │ │ └── zookeeper/ │ │ ├── CMakeLists.txt │ │ ├── ZkResponse.h │ │ ├── ZookeeperContext.cc │ │ ├── ZookeeperContext.h │ │ ├── proto.h │ │ ├── recordio.c │ │ ├── recordio.h │ │ ├── zookeeper.jute.c │ │ └── zookeeper.jute.h │ └── qedis_proxy/ │ ├── CMakeLists.txt │ ├── ClientConn.cc │ ├── ClientConn.h │ ├── ClientManager.cc │ ├── ClientManager.h │ ├── ClusterManager.cc │ ├── ClusterManager.h │ ├── Command.cc │ ├── Command.h │ ├── ConfigParser.cc │ ├── ConfigParser.h │ ├── Protocol.cc │ ├── Protocol.h │ ├── ProxyConfig.cc │ ├── ProxyConfig.h │ ├── ProxyLog.h │ ├── QedisConn.cc │ ├── QedisConn.h │ ├── QedisManager.cc │ ├── QedisManager.h │ ├── ZookeeperConn.cc │ ├── ZookeeperConn.h │ └── main.cc └── qedis.conf ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # MAC OS .DS_Store # build/ bin/ ================================================ FILE: .travis.yml ================================================ sudo: false language: cpp compiler: - clang script: make branches: only: - master notifications: recipients: - bertyoung666@gmail.com email: on_success: change on_failure: always os: - osx ================================================ FILE: CMakeCommon ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++0x") SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -g -Wall -std=c++0x") ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions") SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions") ELSE() message(FATAL_ERROR "Only support linux or OS X, support for windows is in plan") ENDIF() OPTION(DEBUG "Debug or release" OFF) IF(DEBUG) SET(CMAKE_BUILD_TYPE "Debug") ELSE() SET(CMAKE_BUILD_TYPE "Release") ENDIF() ================================================ FILE: CMakeLists.txt ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(QEDIS) SUBDIRS(QBase) SUBDIRS(QedisCore) SUBDIRS(QedisSvr) SUBDIRS(Modules) SUBDIRS(UnitTest) SET(QEDIS_CLUSTER 0) SET(USE_ZOOKEEPER 0) ADD_DEFINITIONS(-DQEDIS_CLUSTER=${QEDIS_CLUSTER}) ADD_DEFINITIONS(-DUSE_ZOOKEEPER=${USE_ZOOKEEPER}) if(${QEDIS_CLUSTER} EQUAL 1) SUBDIRS(QSentinel) endif() ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Bert Young Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ all: @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 cd leveldb && make && cd - mkdir -p bin && ln -s -f ../leveldb/libleveldb.so.1.18 bin/libleveldb.so.1 mkdir -p build && cd build && cmake .. && make clean: @if [ -d "build" ]; then cd build && make clean && cd -; fi cleanall:clean @if [ -d "leveldb" ]; then cd leveldb && make clean && cd -; rm -rf leveldb; fi ================================================ FILE: Modules/CMakeLists.txt ================================================ INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) AUX_SOURCE_DIRECTORY(. QEDIS_MODULE_SRC) LINK_DIRECTORIES(../../leveldb) SET(LIBRARY_OUTPUT_PATH ../../bin) SET(QEDIS_MODULE qedismodule) ADD_LIBRARY(${QEDIS_MODULE} SHARED ${QEDIS_MODULE_SRC}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase) ADD_DEPENDENCIES(${QEDIS_MODULE} qediscore leveldb) # clang needs below, not necessary for gcc TARGET_LINK_LIBRARIES(${QEDIS_MODULE} qbaselib; qediscore; leveldb) SET_TARGET_PROPERTIES(${QEDIS_MODULE} PROPERTIES LINKER_LANGUAGE CXX) ================================================ FILE: Modules/QHashModule.cc ================================================ #include "QHashModule.h" #include "QHash.h" #include "QStore.h" #include "QGlobRegex.h" using namespace qedis; QError hgets(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_hash); if (err != QError_ok) { ReplyError(err, reply); return err; } const PHASH& hash= value->CastHash(); std::vector res; for (const auto& kv : *hash) { if (glob_match(params[2], kv.first)) { res.push_back(&kv.first); res.push_back(&kv.second); } } PreFormatMultiBulk(res.size(), reply); for (auto v : res) { FormatBulk(*v, reply); } return QError_ok; } ================================================ FILE: Modules/QHashModule.h ================================================ #ifndef BERT_QHASHMODULE_H #define BERT_QHASHMODULE_H #include #include "QString.h" #include "QCommon.h" namespace qedis { class UnboundedBuffer; } // hgets hash_key filter_pattern // for example, `hgets profile c*y` may return ["city":"shanghai", "country":"china"] // because c*y matches "city" and "country" // extern "C" qedis::QError hgets(const std::vector& params, qedis::UnboundedBuffer* reply); #endif ================================================ FILE: Modules/QListModule.cc ================================================ #include "QListModule.h" #include "QList.h" #include "QStore.h" #include using namespace qedis; QError ldel(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { Format0(reply); return err; } long idx; if (!Strtol(params[2].c_str(), params[2].size(), &idx)) { ReplyError(QError_nan, reply); return QError_nan; } const PLIST& list = value->CastList(); const int size = static_cast(list->size()); if (idx < 0) idx += size; if (idx < 0 || idx >= size) { Format0(reply); return QError_nop; } if (2 * idx < size) { auto it = list->begin(); std::advance(it, idx); list->erase(it); } else { auto it = list->rbegin(); idx = size - 1 - idx; std::advance(it, idx); list->erase((++it).base()); } if (list->empty()) QSTORE.DeleteKey(params[1]); Format1(reply); return QError_ok; } ================================================ FILE: Modules/QListModule.h ================================================ #ifndef BERT_QLISTMODULE_H #define BERT_QLISTMODULE_H #include #include "QString.h" #include "QCommon.h" namespace qedis { class UnboundedBuffer; } // ldel list_key item_index // For delete list item by index // extern "C" qedis::QError ldel(const std::vector& params, qedis::UnboundedBuffer* reply); #endif ================================================ FILE: Modules/QModuleInit.cc ================================================ #include "QModuleInit.h" #include "QCommand.h" #include extern "C" qedis::QError ldel(const std::vector& params, qedis::UnboundedBuffer* reply); extern "C" qedis::QError hgets(const std::vector& params, qedis::UnboundedBuffer* reply); extern "C" qedis::QError skeys(const std::vector& params, qedis::UnboundedBuffer* reply); bool QedisModule_OnLoad() { printf("enter %s\n", __FUNCTION__); using namespace qedis; // register list ldel command static QCommandInfo ldelinfo; ldelinfo.cmd = "ldel"; ldelinfo.attr = QAttr_write; ldelinfo.params = 3; ldelinfo.handler = &ldel; if (!QCommandTable::AddCommand("ldel", &ldelinfo)) return false; // register hash hgets command static QCommandInfo hgetsinfo; hgetsinfo.cmd = "hgets"; hgetsinfo.attr = QAttr_read; hgetsinfo.params = 3; hgetsinfo.handler = &hgets; if (!QCommandTable::AddCommand("hgets", &hgetsinfo)) { QCommandTable::DelCommand("ldel"); return false; } // register set skeys command static QCommandInfo skeysinfo; skeysinfo.cmd = "skeys"; skeysinfo.attr = QAttr_read; skeysinfo.params = 3; skeysinfo.handler = &skeys; if (!QCommandTable::AddCommand("skeys", &skeysinfo)) { QCommandTable::DelCommand("hgets"); QCommandTable::DelCommand("ldel"); return false; } printf("exit %s\n", __FUNCTION__); return true; } void QedisModule_OnUnLoad() { printf("enter %s\n", __FUNCTION__); qedis::QCommandTable::DelCommand("skeys"); qedis::QCommandTable::DelCommand("hgets"); qedis::QCommandTable::DelCommand("ldel"); printf("exit %s\n", __FUNCTION__); } ================================================ FILE: Modules/QModuleInit.h ================================================ #ifndef BERT_QMODULEINIT_H #define BERT_QMODULEINIT_H extern "C" bool QedisModule_OnLoad(); extern "C" void QedisModule_OnUnLoad(); #endif ================================================ FILE: Modules/QSetModule.cc ================================================ #include "QSetModule.h" #include "QSet.h" #include "QStore.h" #include "QGlobRegex.h" using namespace qedis; QError skeys(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_set); if (err != QError_ok) { if (err == QError_notExist) FormatNull(reply); else ReplyError(err, reply); return err; } std::vector res; const PSET& set = value->CastSet(); for (const auto& k : *set) { if (glob_match(params[2], k)) { res.push_back(&k); } } PreFormatMultiBulk(res.size(), reply); for (auto v : res) { FormatBulk(*v, reply); } return QError_ok; } ================================================ FILE: Modules/QSetModule.h ================================================ #ifndef BERT_QSETMODULE_H #define BERT_QSETMODULE_H #include #include "QString.h" #include "QCommon.h" namespace qedis { class UnboundedBuffer; } // skeys set_key filter_pattern // for example, `skeys cities sh*` may return ["shanghai", "shenzhen"] // extern "C" qedis::QError skeys(const std::vector& params, qedis::UnboundedBuffer* reply); #endif ================================================ FILE: QBase/AsyncBuffer.cc ================================================ #include #if defined(__APPLE__) #include #endif #include "AsyncBuffer.h" using std::size_t; AsyncBuffer::AsyncBuffer(size_t size) : buffer_(size), backBytes_(0) { } AsyncBuffer::~AsyncBuffer() { // assert (buffer_.IsEmpty()); // assert (backBytes_ == 0); } void AsyncBuffer::Write(const void* data, size_t len) { BufferSequence bf; bf.buffers[0].iov_base = const_cast(data); bf.buffers[0].iov_len = len; bf.count = 1; this->Write(bf); } void AsyncBuffer::Write(const BufferSequence& data) { auto len = data.TotalBytes(); if (backBytes_ > 0 || buffer_.WritableSize() < len) { std::lock_guard guard(backBufLock_); if (backBytes_ > 0 || buffer_.WritableSize() < len) { for (size_t i = 0; i < data.count; ++ i) { backBuf_.PushData(data.buffers[i].iov_base, data.buffers[i].iov_len); } backBytes_ += len; assert (backBytes_ == backBuf_.ReadableSize()); return; } } assert(backBytes_ == 0 && buffer_.WritableSize() >= len); for (size_t i = 0; i < data.count; ++ i) { buffer_.PushData(data.buffers[i].iov_base, data.buffers[i].iov_len); } } void AsyncBuffer::ProcessBuffer(BufferSequence& data) { data.count = 0; // Here be dragons! see below... if (!tmpBuf_.IsEmpty()) { data.count = 1; data.buffers[0].iov_base = tmpBuf_.ReadAddr(); data.buffers[0].iov_len = tmpBuf_.ReadableSize(); } else if (!buffer_.IsEmpty()) { auto nLen = buffer_.ReadableSize(); buffer_.GetDatum(data, nLen); assert (nLen == data.TotalBytes()); } else { if (backBytes_ > 0 && backBufLock_.try_lock()) { // tmpBuf_ is used for process backBuf_ without held mutex! backBytes_ = 0; tmpBuf_.Swap(backBuf_); backBufLock_.unlock(); data.count = 1; data.buffers[0].iov_base = tmpBuf_.ReadAddr(); data.buffers[0].iov_len = tmpBuf_.ReadableSize(); } } } void AsyncBuffer::Skip(size_t size) { if (!tmpBuf_.IsEmpty()) { assert(size <= tmpBuf_.ReadableSize()); tmpBuf_.AdjustReadPtr(size); } else { assert(buffer_.ReadableSize() >= size); buffer_.AdjustReadPtr(size); } } ================================================ FILE: QBase/AsyncBuffer.h ================================================ #ifndef BERT_ASYNCBUFFER_H #define BERT_ASYNCBUFFER_H #include #include #include "Buffer.h" #include "UnboundedBuffer.h" class AsyncBuffer { public: explicit AsyncBuffer(std::size_t size = 128 * 1024); ~AsyncBuffer(); void Write(const void* data, std::size_t len); void Write(const BufferSequence& data); void ProcessBuffer(BufferSequence& data); void Skip(std::size_t size); private: // for async write Buffer buffer_; // double buffer qedis::UnboundedBuffer tmpBuf_; std::mutex backBufLock_; std::atomic backBytes_; qedis::UnboundedBuffer backBuf_; }; #endif ================================================ FILE: QBase/Buffer.h ================================================ #ifndef BERT_BUFFER_H #define BERT_BUFFER_H #include #include #include #include #include #include struct BufferSequence { static const std::size_t kMaxIovec = 16; iovec buffers[kMaxIovec]; std::size_t count; std::size_t TotalBytes() const { assert (count <= kMaxIovec); std::size_t nBytes = 0; for (std::size_t i = 0; i < count; ++ i) nBytes += buffers[i].iov_len; return nBytes; } }; //static const std::size_t DEFAULT_BUFFER_SIZE = 4 * 1024; inline std::size_t RoundUp2Power(std::size_t size) { if (0 == size) return 0; std::size_t roundSize = 1; while (roundSize < size) roundSize <<= 1; return roundSize; } template class CircularBuffer { public: // Constructor to be specialized explicit CircularBuffer(std::size_t size = 0) : maxSize_(size), readPos_(0), writePos_(0) { } CircularBuffer(const BufferSequence& bf); CircularBuffer(char* , std::size_t ); ~CircularBuffer() { } bool IsEmpty() const { return writePos_ == readPos_; } bool IsFull() const { return ((writePos_ + 1) & (maxSize_ - 1)) == readPos_; } // For gather write void GetDatum(BufferSequence& buffer, std::size_t maxSize, std::size_t offset = 0); // For scatter read void GetSpace(BufferSequence& buffer, std::size_t offset = 0); // Put data into internal buffer_ bool PushData(const void* pData, std::size_t nSize); bool PushDataAt(const void* pData, std::size_t nSize, std::size_t offset = 0); // Get data from internal buffer_, adjust read ptr bool PeekData(void* pBuf, std::size_t nSize); bool PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset = 0); char* ReadAddr() { return &buffer_[readPos_]; } char* WriteAddr() { return &buffer_[writePos_]; } void AdjustWritePtr(std::size_t size); void AdjustReadPtr(std::size_t size); std::size_t ReadableSize() const { return (writePos_ - readPos_) & (maxSize_ - 1); } std::size_t WritableSize() const { return maxSize_ - ReadableSize() - 1; } std::size_t Capacity() const { return maxSize_; } void InitCapacity(std::size_t size); template CircularBuffer& operator<< (const T& data); template CircularBuffer& operator>> (T& data); template CircularBuffer & operator<< (const std::vector& ); template CircularBuffer & operator>> (std::vector& ); CircularBuffer & operator<< (const std::string& str); CircularBuffer & operator>> (std::string& str); protected: // The max capacity of buffer_ std::size_t maxSize_; private: // The starting address can be read std::atomic readPos_; // The starting address can be write std::atomic writePos_; // The real internal buffer BUFFER buffer_; bool owned_ = false; }; template void CircularBuffer::GetDatum(BufferSequence& buffer, std::size_t maxSize, std::size_t offset) { if (maxSize == 0 || offset >= ReadableSize() ) { buffer.count = 0; return; } assert(readPos_ < maxSize_); assert(writePos_ < maxSize_); std::size_t bufferIndex = 0; const std::size_t readPos = (readPos_ + offset) & (maxSize_ - 1); const std::size_t writePos = writePos_; assert (readPos != writePos); buffer.buffers[bufferIndex].iov_base = &buffer_[readPos]; if (readPos < writePos) { if (maxSize < writePos - readPos) buffer.buffers[bufferIndex].iov_len = maxSize; else buffer.buffers[bufferIndex].iov_len = writePos - readPos; } else { std::size_t nLeft = maxSize; if (nLeft > (maxSize_ - readPos)) nLeft = (maxSize_ - readPos); buffer.buffers[bufferIndex].iov_len = nLeft; nLeft = maxSize - nLeft; if (nLeft > 0 && writePos > 0) { if (nLeft > writePos) nLeft = writePos; ++ bufferIndex; buffer.buffers[bufferIndex].iov_base = &buffer_[0]; buffer.buffers[bufferIndex].iov_len = nLeft; } } buffer.count = bufferIndex + 1; } template void CircularBuffer::GetSpace(BufferSequence& buffer, std::size_t offset) { assert(readPos_ >= 0 && readPos_ < maxSize_); assert(writePos_ >= 0 && writePos_ < maxSize_); if (WritableSize() <= offset + 1) { buffer.count = 0; return; } std::size_t bufferIndex = 0; const std::size_t readPos = readPos_; const std::size_t writePos = (writePos_ + offset) & (maxSize_ - 1); buffer.buffers[bufferIndex].iov_base = &buffer_[writePos]; if (readPos > writePos) { buffer.buffers[bufferIndex].iov_len = readPos - writePos - 1; assert (buffer.buffers[bufferIndex].iov_len > 0); } else { buffer.buffers[bufferIndex].iov_len = maxSize_ - writePos; if (0 == readPos) { buffer.buffers[bufferIndex].iov_len -= 1; } else if (readPos > 1) { ++ bufferIndex; buffer.buffers[bufferIndex].iov_base = &buffer_[0]; buffer.buffers[bufferIndex].iov_len = readPos - 1; } } buffer.count = bufferIndex + 1; } template bool CircularBuffer::PushDataAt(const void* pData, std::size_t nSize, std::size_t offset) { if (!pData || 0 == nSize) return true; if (offset + nSize > WritableSize()) return false; const std::size_t readPos = readPos_; const std::size_t writePos = (writePos_ + offset) & (maxSize_ - 1); if (readPos > writePos) { assert(readPos - writePos > nSize); ::memcpy(&buffer_[writePos], pData, nSize); } else { std::size_t availBytes1 = maxSize_ - writePos; std::size_t availBytes2 = readPos - 0; assert (availBytes1 + availBytes2 >= 1 + nSize); if (availBytes1 >= nSize + 1) { ::memcpy(&buffer_[writePos], pData, nSize); } else { ::memcpy(&buffer_[writePos], pData, availBytes1); int bytesLeft = static_cast(nSize - availBytes1); if (bytesLeft > 0) ::memcpy(&buffer_[0], static_cast(pData) + availBytes1, bytesLeft); } } return true; } template bool CircularBuffer::PushData(const void* pData, std::size_t nSize) { if (!PushDataAt(pData, nSize)) return false; AdjustWritePtr(nSize); return true; } template bool CircularBuffer::PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset) { if (!pBuf || 0 == nSize) return true; if (nSize + offset > ReadableSize()) return false; const std::size_t writePos = writePos_; const std::size_t readPos = (readPos_ + offset) & (maxSize_ - 1); if (readPos < writePos) { assert(writePos - readPos >= nSize); ::memcpy(pBuf, &buffer_[readPos], nSize); } else { assert(readPos > writePos); std::size_t availBytes1 = maxSize_ - readPos; std::size_t availBytes2 = writePos - 0; assert(availBytes1 + availBytes2 >= nSize); if (availBytes1 >= nSize) { ::memcpy(pBuf, &buffer_[readPos], nSize); } else { ::memcpy(pBuf, &buffer_[readPos], availBytes1); assert(nSize - availBytes1 > 0); ::memcpy(static_cast(pBuf) + availBytes1, &buffer_[0], nSize - availBytes1); } } return true; } template bool CircularBuffer::PeekData(void* pBuf, std::size_t nSize) { if (PeekDataAt(pBuf, nSize)) AdjustReadPtr(nSize); else return false; return true; } template inline void CircularBuffer::AdjustWritePtr(std::size_t size) { std::size_t writePos = writePos_; writePos += size; writePos &= maxSize_ - 1; writePos_ = writePos; } template inline void CircularBuffer::AdjustReadPtr(std::size_t size) { std::size_t readPos = readPos_; readPos += size; readPos &= maxSize_ - 1; readPos_ = readPos; } template inline void CircularBuffer::InitCapacity(std::size_t size) { assert (size > 0 && size <= 1 * 1024 * 1024 * 1024); maxSize_ = RoundUp2Power(size); buffer_.resize(maxSize_); std::vector(buffer_).swap(buffer_); } template template inline CircularBuffer& CircularBuffer::operator<< (const T& data ) { if (!PushData(&data, sizeof(data))) assert (!!!"Please modify the DEFAULT_BUFFER_SIZE"); return *this; } template template inline CircularBuffer & CircularBuffer::operator>> (T& data ) { if (!PeekData(&data, sizeof(data))) assert(!!!"Not enough data in buffer_"); return *this; } template template inline CircularBuffer & CircularBuffer::operator<< (const std::vector& v) { if (!v.empty()) { (*this) << static_cast(v.size()); for ( typename std::vector::const_iterator it = v.begin(); it != v.end(); ++it ) { (*this) << *it; } } return *this; } template template inline CircularBuffer & CircularBuffer::operator>> (std::vector& v) { v.clear(); unsigned short size; *this >> size; v.reserve(size); while (size--) { T t; *this >> t; v.push_back(t); } return *this; } template inline CircularBuffer& CircularBuffer::operator<< (const std::string& str) { *this << static_cast(str.size()); if (!PushData(str.data(), str.size())) { AdjustWritePtr(static_cast(0 - sizeof(unsigned short))); assert(!!!"2Please modify the DEFAULT_BUFFER_SIZE"); } return *this; } template inline CircularBuffer& CircularBuffer::operator>> (std::string& str) { unsigned short size = 0; *this >> size; str.clear(); str.reserve(size); char ch; while ( size-- ) { *this >> ch; str += ch; } return *this; } /////////////////////////////////////////////////////////////// typedef CircularBuffer< ::std::vector > Buffer; template <> inline Buffer::CircularBuffer(std::size_t maxSize) : maxSize_(RoundUp2Power(maxSize)), readPos_(0), writePos_(0), buffer_(maxSize_) { assert (0 == (maxSize_ & (maxSize_ - 1)) && "maxSize_ MUST BE power of 2"); } template class StackBuffer : public CircularBuffer { using CircularBuffer::maxSize_; public: StackBuffer() { maxSize_ = N; if (maxSize_ < 0) maxSize_ = 1; if (0 != (maxSize_ & (maxSize_ - 1))) maxSize_ = RoundUp2Power(maxSize_); assert (0 == (maxSize_ & (maxSize_ - 1)) && "maxSize_ MUST BE power of 2"); } }; typedef CircularBuffer AttachedBuffer; template <> inline AttachedBuffer::CircularBuffer(char* pBuf, std::size_t len) : maxSize_(RoundUp2Power(len + 1)), readPos_(0), writePos_(len) { buffer_ = pBuf; owned_ = false; } template <> inline AttachedBuffer::CircularBuffer(const BufferSequence& bf) : readPos_(0), writePos_(0) { owned_ = false; if (0 == bf.count) { buffer_ = 0; } else if (1 == bf.count) { buffer_ = (char*)bf.buffers[0].iov_base; writePos_ = static_cast(bf.buffers[0].iov_len); } else if (bf.count > 1) { owned_ = true; buffer_ = new char[bf.TotalBytes()]; std::size_t off = 0; for (std::size_t i = 0; i < bf.count; ++ i) { memcpy(buffer_ + off, bf.buffers[i].iov_base, bf.buffers[i].iov_len); off += bf.buffers[i].iov_len; } writePos_ = bf.TotalBytes(); } maxSize_ = RoundUp2Power(writePos_ - readPos_ + 1); } template <> inline AttachedBuffer::~CircularBuffer() { if (owned_) delete [] buffer_; } template inline void OverwriteAt(void* addr, T data) { memcpy(addr, &data, sizeof(data)); } #endif ================================================ FILE: QBase/CMakeLists.txt ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) AUX_SOURCE_DIRECTORY(. SDK_SRC) AUX_SOURCE_DIRECTORY(./Log SDK_SRC) AUX_SOURCE_DIRECTORY(./Threads SDK_SRC) AUX_SOURCE_DIRECTORY(./SmartPtr SDK_SRC) AUX_SOURCE_DIRECTORY(./lzf SDK_SRC) SET(LIBRARY_OUTPUT_PATH ../../bin) ADD_LIBRARY(qbaselib SHARED ${SDK_SRC}) TARGET_LINK_LIBRARIES(qbaselib; pthread) SET_TARGET_PROPERTIES(qbaselib PROPERTIES LINKER_LANGUAGE CXX) ================================================ FILE: QBase/ClientSocket.cc ================================================ #include #include #include #include #include #include #include "Server.h" #include "ClientSocket.h" #include "Log/Logger.h" #include "NetThreadPool.h" ClientSocket::ClientSocket(int tag) : tag_(tag) { } ClientSocket::~ClientSocket() { if (localSock_ != INVALID_SOCKET) WRN << "Destruct ClientSocket " << localSock_; } bool ClientSocket::Connect(const SocketAddr& dst) { if (dst.Empty()) return false; if (INVALID_SOCKET != localSock_) return false; peerAddr_ = dst; localSock_ = CreateTCPSocket(); SetNonBlock(localSock_); SetNodelay(localSock_); SetRcvBuf(localSock_); SetSndBuf(localSock_); int result = ::connect(localSock_, (sockaddr*)&peerAddr_.GetAddr(), sizeof(sockaddr_in)); if (0 == result) { INF << "ClientSocket immediately connected to [" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << "]"; Server::Instance()->NewConnection(localSock_, tag_, onConnectFail_); localSock_ = INVALID_SOCKET; return true; } else { if (EINPROGRESS == errno) { INF << "EINPROGRESS: ClientSocket connected to (" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << ")"; Internal::NetThreadPool::Instance().AddSocket(shared_from_this(), EventTypeWrite); epollOut_ = true; return true; } ERR << "Failed: ClientSocket connected to (" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << ")"; if (onConnectFail_) onConnectFail_(); return false; } return true; } bool ClientSocket::OnWritable() { epollOut_ = false; int error = 0; socklen_t len = sizeof error; bool bSucc = (::getsockopt(localSock_, SOL_SOCKET, SO_ERROR, (char*)&error, &len) >= 0 && 0 == error); if (!bSucc) { errno = error; ERR << "Failed: ClientSocket connected to (" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << "), error:" << error; return false; } INF << "Success: ClientSocket connected to (" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << ")"; Server::Instance()->NewConnection(localSock_, tag_, onConnectFail_); localSock_ = INVALID_SOCKET; return true; } bool ClientSocket::OnError() { if (Socket::OnError()) { ERR << __FUNCTION__ << " connected to (" << peerAddr_.GetIP() << ":" << peerAddr_.GetPort() << ")"; if (onConnectFail_) onConnectFail_(); return true; } return false; } ================================================ FILE: QBase/ClientSocket.h ================================================ #ifndef BERT_CLIENTSOCKET_H #define BERT_CLIENTSOCKET_H #include #include "Socket.h" // Abstraction for a TCP client socket class ClientSocket : public Socket { public: explicit ClientSocket(int tag); ~ClientSocket(); bool Connect(const SocketAddr& addr); bool OnWritable(); bool OnError(); SocketType GetSockType() const { return SocketType_Client; } void SetFailCallback(const std::function& cb) { onConnectFail_ = cb; } private: const int tag_; SocketAddr peerAddr_; std::function onConnectFail_; }; #endif ================================================ FILE: QBase/ConfigParser.cc ================================================ #include #include "ConfigParser.h" #include "Log/MemoryFile.h" static const int SPACE = ' '; static const int TAB = '\t'; static const int NEWLINE = '\n'; static const int COMMENT = '#'; static size_t SkipBlank(const char* data, size_t len, size_t off) { while (++ off < len) { if (SPACE != data[off] && TAB != data[off]) { -- off; break; } } return off; } bool ConfigParser::Load(const char* FileName) { InputMemoryFile file; if (!file.Open(FileName)) return false; // no such file data_.clear(); size_t maxLen = size_t(-1); const char* data = file.Read(maxLen); bool bReadKey = true; std::string key, value; key.reserve(64); value.reserve(64); size_t off = 0; while (off < maxLen) { switch (data[off]) { case COMMENT: while (++ off < maxLen) { if (NEWLINE == data[off]) { -- off; break; } } break; case NEWLINE: bReadKey = true; if (!key.empty()) { data_[key].push_back(value); key.clear(); value.clear(); } off = SkipBlank(data, maxLen, off); break; case SPACE: case TAB: // 支持value中有空格 if (bReadKey) { bReadKey = false; off = SkipBlank(data, maxLen, off); // 跳过所有分界空格 } else { value += data[off]; } break; case '\r': break; default: if (bReadKey) key += data[off]; else value += data[off]; break; } ++ off; } file.Close(); return true; } const std::vector& ConfigParser::GetDataVector(const char* key) const { auto it = data_.find(key); if (it == data_.end()) { static const std::vector kEmpty; return kEmpty; } return it->second; } #ifdef CONFIG_DEBUG int main() { ConfigParser csv; csv.Load("config"); csv.Print(); std::cout << "=====================" << std::endl; } #endif ================================================ FILE: QBase/ConfigParser.h ================================================ #ifndef BERT_CONFIGPARSER_H #define BERT_CONFIGPARSER_H #include #include #include #ifdef CONFIG_DEBUG #include #endif class ConfigParser { public: bool Load(const char* FileName); template T GetData(const char* key, const T& default_ = T()) const; const std::vector& GetDataVector(const char* key) const; #ifdef CONFIG_DEBUG void Print() { std::cout << "//////////////////"<< std::endl; std::map::const_iterator it = data_.begin(); while (it != data_.end()) { std::cout << it->first << ":" << it->second << "\n"; ++ it; } } #endif private: typedef std::map > Data; Data data_; template T _ToType(const std::string& data) const; }; template inline T ConfigParser::_ToType(const std::string& data) const { T t; std::istringstream os(data); os >> t; return t; } template <> inline const char* ConfigParser::_ToType(const std::string& data) const { return data.c_str(); } template <> inline std::string ConfigParser::_ToType(const std::string& data) const { return data; } template inline T ConfigParser::GetData(const char* key, const T& default_) const { auto it = data_.find(key); if (it == data_.end()) return default_; return _ToType(it->second[0]); // only return first value } #endif ================================================ FILE: QBase/Delegate.h ================================================ #include #include template class Delegate; template class Delegate { public: typedef Delegate Self; Delegate() = default; Delegate(const Self& ) = delete; Self& operator= (const Self& ) = delete; template Delegate(F&& f) { connect(std::forward(f)); } Delegate(Self&& other) : funcs_(std::move(other.funcs_)) { } template Self& operator+=(F&& f) { connect(std::forward(f)); return *this; } template Self& operator-=(F&& f) { disconnect(std::forward(f)); return *this; } void operator()(Args&&... args) { call(std::forward(args)...); } private: std::list > funcs_; template void connect(F&& f) { funcs_.emplace_back(std::forward(f)); } template void disconnect(F&& f) { //std::cerr << "&f = " << typeid(&f).name() << ", and target = " << typeid(decltype(std::addressof(f))).name() << std::endl; for (auto it(funcs_.begin()); it != funcs_.end(); ++ it) { const auto& target = it->template target(); if (target) { if(*target == &f) { funcs_.erase(it); return; } } else { const auto& target2 = it->template target::type>(); // the function object must implement operator == if (target2 && *target2 == f) { funcs_.erase(it); return; } } } } void call(Args&&... args) { // But what if rvalue args? FIXME for (const auto& f : funcs_) f(std::forward(args)...); } }; ================================================ FILE: QBase/EPoller.cc ================================================ #if defined(__gnu_linux__) #include "EPoller.h" #include "Log/Logger.h" #include #include namespace Epoll { bool ModSocket(int epfd, int socket, uint32_t events, void* ptr); bool AddSocket(int epfd, int socket, uint32_t events, void* ptr) { if (socket < 0) return false; epoll_event ev; ev.data.ptr= ptr; ev.events = 0; if (events & EventTypeRead) ev.events |= EPOLLIN; if (events & EventTypeWrite) ev.events |= EPOLLOUT; return 0 == epoll_ctl(epfd, EPOLL_CTL_ADD, socket, &ev); } bool DelSocket(int epfd, int socket) { if (socket < 0) return false; epoll_event dummy; return 0 == epoll_ctl(epfd, EPOLL_CTL_DEL, socket, &dummy) ; } bool ModSocket(int epfd, int socket, uint32_t events, void* ptr) { if (socket < 0) return false; epoll_event ev; ev.data.ptr= ptr; ev.events = 0; if (events & EventTypeRead) ev.events |= EPOLLIN; if (events & EventTypeWrite) ev.events |= EPOLLOUT; return 0 == epoll_ctl(epfd, EPOLL_CTL_MOD, socket, &ev); } } Epoller::Epoller() { multiplexer_ = ::epoll_create(512); INF << "create epoll: " << multiplexer_; } Epoller::~Epoller() { INF << "close epoll: " << multiplexer_; if (multiplexer_ != -1) ::close(multiplexer_); } bool Epoller::AddSocket(int sock, int events, void* userPtr) { if (Epoll::AddSocket(multiplexer_, sock, events, userPtr)) return true; return (errno == EEXIST) && ModSocket(sock, events, userPtr); } bool Epoller::DelSocket(int sock, int events) { return Epoll::DelSocket(multiplexer_, sock); } bool Epoller::ModSocket(int sock, int events, void* userPtr) { if (events == 0) return DelSocket(sock, 0); if (Epoll::ModSocket(multiplexer_, sock, events, userPtr)) return true; return errno == ENOENT && AddSocket(sock, events, userPtr); } int Epoller::Poll(std::vector& events, size_t maxEvent, int timeoutMs) { if (maxEvent == 0) return 0; while (events_.size() < maxEvent) events_.resize(2 * events_.size() + 1); int nFired = TEMP_FAILURE_RETRY(::epoll_wait(multiplexer_, &events_[0], maxEvent, timeoutMs)); if (nFired == -1 && errno != EINTR && errno != EWOULDBLOCK) return -1; if (nFired > 0 && static_cast(nFired) > events.size()) events.resize(nFired); for (int i = 0; i < nFired; ++ i) { FiredEvent& fired = events[i]; fired.events = 0; fired.userdata = events_[i].data.ptr; if (events_[i].events & EPOLLIN) fired.events |= EventTypeRead; if (events_[i].events & EPOLLOUT) fired.events |= EventTypeWrite; if (events_[i].events & (EPOLLERR | EPOLLHUP)) fired.events |= EventTypeError; } return nFired; } #endif ================================================ FILE: QBase/EPoller.h ================================================ #ifndef BERT_EPOLLER_H #define BERT_EPOLLER_H #if defined(__gnu_linux__) #include #include #include "Poller.h" class Epoller : public Poller { public: Epoller(); ~Epoller(); bool AddSocket(int sock, int events, void* userPtr); bool ModSocket(int sock, int events, void* userPtr); bool DelSocket(int sock, int events); int Poll(std::vector& events, std::size_t maxEvent, int timeoutMs); private: std::vector events_; }; #endif #endif ================================================ FILE: QBase/Kqueue.cc ================================================ #if defined(__APPLE__) #include "Kqueue.h" #include "Log/Logger.h" #include #include #include Kqueue::Kqueue() { multiplexer_ = ::kqueue(); INF << "create kqueue: " << multiplexer_; } Kqueue::~Kqueue() { INF << "close kqueue: " << multiplexer_; if (multiplexer_ != -1) ::close(multiplexer_); } bool Kqueue::AddSocket(int sock, int events, void* userPtr) { struct kevent change[2]; int cnt = 0; if (events & EventTypeRead) { EV_SET(change + cnt, sock, EVFILT_READ, EV_ADD, 0, 0, userPtr); ++ cnt; } if (events & EventTypeWrite) { EV_SET(change + cnt, sock, EVFILT_WRITE, EV_ADD, 0, 0, userPtr); ++ cnt; } return kevent(multiplexer_, change, cnt, NULL, 0, NULL) != -1; } bool Kqueue::DelSocket(int sock, int events) { struct kevent change[2]; int cnt = 0; if (events & EventTypeRead) { EV_SET(change + cnt, sock, EVFILT_READ, EV_DELETE, 0, 0, NULL); ++ cnt; } if (events & EventTypeWrite) { EV_SET(change + cnt, sock, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); ++ cnt; } if (cnt == 0) return false; return -1 != kevent(multiplexer_, change, cnt, NULL, 0, NULL); } bool Kqueue::ModSocket(int sock, int events, void* userPtr) { bool ret = DelSocket(sock, EventTypeRead | EventTypeWrite); if (events == 0) return ret; return AddSocket(sock, events, userPtr); } int Kqueue::Poll(std::vector& events, std::size_t maxEvent, int timeoutMs) { if (maxEvent == 0) return 0; while (events_.size() < maxEvent) events_.resize(2 * events_.size() + 1); struct timespec* pTimeout = NULL; struct timespec timeout; if (timeoutMs >= 0) { pTimeout = &timeout; timeout.tv_sec = timeoutMs / 1000; timeout.tv_nsec = timeoutMs % 1000 * 1000000; } int nFired = ::kevent(multiplexer_, NULL, 0, &events_[0], static_cast(maxEvent), pTimeout); if (nFired == -1) return -1; if (nFired > 0 && static_cast(nFired) > events.size()) events.resize(nFired); for (int i = 0; i < nFired; ++ i) { FiredEvent& fired = events[i]; fired.events = 0; fired.userdata = events_[i].udata; if (events_[i].filter == EVFILT_READ) fired.events |= EventTypeRead; if (events_[i].filter == EVFILT_WRITE) fired.events |= EventTypeWrite; if (events_[i].flags & EV_ERROR) fired.events |= EventTypeError; } return nFired; } #endif ================================================ FILE: QBase/Kqueue.h ================================================ #ifndef BERT_KQUEUE_H #define BERT_KQUEUE_H #if defined(__APPLE__) #include "Poller.h" #include class Kqueue : public Poller { public: Kqueue(); ~Kqueue(); bool AddSocket(int sock, int events, void* userPtr); bool ModSocket(int sock, int events, void* userPtr); bool DelSocket(int sock, int events); int Poll(std::vector& events, std::size_t maxEvent, int timeoutMs); private: std::vector events_; }; #endif #endif ================================================ FILE: QBase/ListenSocket.cc ================================================ #include #include #include #include #include #include "Server.h" #include "NetThreadPool.h" #include "ListenSocket.h" #include "Log/Logger.h" namespace Internal { const int ListenSocket::LISTENQ = 1024; ListenSocket::ListenSocket(int tag) : localPort_(INVALID_PORT), tag_(tag) { } ListenSocket::~ListenSocket() { Server::Instance()->DelListenSock(localSock_); USR << "Close ListenSocket " << localSock_; } bool ListenSocket::Bind(const SocketAddr& addr) { if (addr.Empty()) return false; if (localSock_ != INVALID_SOCKET) return false; localPort_ = addr.GetPort(); localSock_ = CreateTCPSocket(); SetNonBlock(localSock_); SetNodelay(localSock_); SetReuseAddr(localSock_); SetRcvBuf(localSock_); SetSndBuf(localSock_); struct sockaddr_in serv = addr.GetAddr(); int ret = ::bind(localSock_, (struct sockaddr*)&serv, sizeof serv); if (SOCKET_ERROR == ret) { CloseSocket(localSock_); ERR << "Cannot bind port " << addr.GetPort(); return false; } ret = ::listen(localSock_, ListenSocket::LISTENQ); if (SOCKET_ERROR == ret) { CloseSocket(localSock_); ERR << "Cannot listen on port " << addr.GetPort(); return false; } if (!NetThreadPool::Instance().AddSocket(shared_from_this(), EventTypeRead)) return false; USR << "Success: listen on (" << addr.GetIP() << ":" << addr.GetPort() << ")"; return true; } int ListenSocket::_Accept() { socklen_t addrLength = sizeof addrClient_; return ::accept(localSock_, (struct sockaddr *)&addrClient_, &addrLength); } bool ListenSocket::OnReadable() { while (true) { int connfd = _Accept(); if (connfd >= 0) { Server::Instance()->NewConnection(connfd, tag_); } else { bool result = false; switch (errno) { case EWOULDBLOCK: case ECONNABORTED: case EINTR: result = true; break; case EMFILE: case ENFILE: ERR << "Not enough file descriptor available!!!"; result = true; break; case ENOBUFS: ERR << "Not enough memory"; result = true; default: ERR << "When accept, unknown error happened : " << errno; break; } return result; } } return true; } bool ListenSocket::OnWritable() { return false; } bool ListenSocket::OnError() { if (Socket::OnError()) { Server::Instance()->DelListenSock(localSock_); return true; } return false; } } ================================================ FILE: QBase/ListenSocket.h ================================================ #ifndef BERT_LISTENSOCKET_H #define BERT_LISTENSOCKET_H #include "Socket.h" namespace Internal { class ListenSocket : public Socket { static const int LISTENQ; public: explicit ListenSocket(int tag); ~ListenSocket(); SocketType GetSocketType() const { return SocketType_Listen; } bool Bind(const SocketAddr& addr); bool OnReadable(); bool OnWritable(); bool OnError(); private: int _Accept(); sockaddr_in addrClient_; unsigned short localPort_; const int tag_; }; } #endif ================================================ FILE: QBase/Log/Logger.cc ================================================ #include #include #include #include #include #include #include enum LogColor { RED_COLOR = 1, GREEN_COLOR , YELLOW_COLOR , NORMAL_COLOR , BLUE_COLOR , PURPLE_COLOR , WHITE_COLOR , COLOR_MAX , } ; #include "Logger.h" #include "../Timer.h" static const size_t DEFAULT_LOGFILESIZE = 32 * 1024 * 1024; static const size_t PREFIX_LEVEL_LEN = 6; static const size_t PREFIX_TIME_LEN = 24; unsigned int ConvertLogLevel(const std::string& level) { unsigned int l = logALL; if (level == "debug") { ; } else if (level == "verbose") { l &= ~logDEBUG; } else if (level == "notice") { l &= ~logDEBUG; l &= ~logINFO; } else if (level == "warning") { l &= ~logDEBUG; l &= ~logINFO; l &= ~logWARN; // redis warning is my error } else if (level == "none") { l = 0; } return l; } static bool MakeDir(const char* pDir) { if (mkdir(pDir, 0755) != 0) { if (EEXIST != errno) return false; } return true; } __thread Logger* g_log = nullptr; __thread unsigned g_logLevel; __thread unsigned g_logDest; Logger::Logger() : level_(0), dest_(0) { lastLogMSecond_ = lastLogSecond_ = -1; _Reset(); } Logger::~Logger() { _CloseLogFile(); } bool Logger::Init(unsigned int level, unsigned int dest, const char* pDir) { level_ = level; dest_ = dest; directory_ = pDir ? pDir : "."; if (0 == level_) { return true; } if (dest_ & logFILE) { if (directory_ == "." || MakeDir(directory_.c_str())) { _OpenLogFile(_MakeFileName().c_str()); return true; } return false; } if (!(dest_ & logConsole)) { std::cerr << "log has no output, but loglevel is " << level << std::endl; return false; } return true; } bool Logger::_CheckChangeFile() { if (!file_.IsOpen()) return true; return file_.Offset() + MAXLINE_LOG > DEFAULT_LOGFILESIZE; } const std::string& Logger::_MakeFileName() { char name[32]; Time now; size_t len = now.FormatTime(name); name[len] = '\0'; fileName_ = directory_ + "/" + name; fileName_ += ".log"; return fileName_; } bool Logger::_OpenLogFile(const char* name) { return file_.Open(name, true); } void Logger::_CloseLogFile() { return file_.Close(); } void Logger::Flush(enum LogLevel level) { if (IsLevelForbid(curLevel_) || !(level & curLevel_) || (pos_ <= PREFIX_TIME_LEN + PREFIX_LEVEL_LEN)) { _Reset(); return; } time_.Now(); #if 0 time_.FormatTime(tmpBuffer_); #else auto seconds = time_.MilliSeconds() / 1000; if (seconds != lastLogSecond_) { time_.FormatTime(tmpBuffer_); lastLogSecond_ = seconds; } else { auto msec = time_.MilliSeconds() % 1000; if (msec != lastLogMSecond_) { snprintf(tmpBuffer_ + 20, 4, "%03d", static_cast(msec)); tmpBuffer_[23] = ']'; lastLogMSecond_ = msec; } } #endif switch(level) { case logINFO: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[INF]:", PREFIX_LEVEL_LEN); break; case logDEBUG: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[DBG]:", PREFIX_LEVEL_LEN); break; case logWARN: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[WRN]:", PREFIX_LEVEL_LEN); break; case logERROR: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[ERR]:", PREFIX_LEVEL_LEN); break; case logUSR: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[USR]:", PREFIX_LEVEL_LEN); break; default: memcpy(tmpBuffer_ + PREFIX_TIME_LEN, "[???]:", PREFIX_LEVEL_LEN); break; } tmpBuffer_[pos_ ++] = '\n'; tmpBuffer_[pos_] = '\0'; if (LogManager::Instance().IsShutdown()) { std::cout << tmpBuffer_; } else { // Format: level info, length info, log msg int logLevel = level; BufferSequence contents; contents.count = 3; contents.buffers[0].iov_base = &logLevel; contents.buffers[0].iov_len = sizeof logLevel; contents.buffers[1].iov_base = &pos_; contents.buffers[1].iov_len = sizeof pos_; contents.buffers[2].iov_base = tmpBuffer_; contents.buffers[2].iov_len = pos_; buffer_.Write(contents); } _Reset(); } void Logger::_Color(unsigned int color) { #if defined(__gnu_linux__) const char* colorstrings[COLOR_MAX] = { "", "\033[1;31;40m", "\033[1;32;40m", "\033[1;33;40m", "\033[0m", "\033[1;34;40m", "\033[1;35;40m", "\033[1;37;40m", }; fprintf(stdout, "%s", colorstrings[color]); #endif } Logger& Logger::operator<< (const char* msg) { if (IsLevelForbid(curLevel_)) { return *this; } const auto len = strlen(msg); if (pos_ + len >= MAXLINE_LOG) { return *this; } memcpy(tmpBuffer_ + pos_, msg, len); pos_ += len; return *this; } Logger& Logger::operator<< (const unsigned char* msg) { return operator<<(reinterpret_cast(msg)); } Logger& Logger::operator<< (const std::string& msg) { return operator<<(msg.c_str()); } Logger& Logger::operator<< (void* ptr) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 18 < MAXLINE_LOG) { unsigned long ptrValue = (unsigned long)ptr; auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%#018lx", ptrValue); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (unsigned char a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 3 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%hhd", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (char a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 3 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%hhu", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (unsigned short a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 5 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%hu", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (short a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 5 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%hd", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (unsigned int a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 10 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%u", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (int a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 10 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%d", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (unsigned long a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 20 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%lu", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (long a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 20 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%ld", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (unsigned long long a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 20 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%llu", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (long long a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 20 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%lld", a); if (nbytes > 0) pos_ += nbytes; } return *this; } Logger& Logger::operator<< (double a) { if (IsLevelForbid(curLevel_)) { return *this; } if (pos_ + 20 < MAXLINE_LOG) { auto nbytes = snprintf(tmpBuffer_ + pos_, MAXLINE_LOG - pos_, "%.6g", a); if (nbytes > 0) pos_ += nbytes; } return *this; } bool Logger::Update() { BufferSequence data; buffer_.ProcessBuffer(data); AttachedBuffer abf(data); auto nWritten = _Log(abf.ReadAddr(), abf.ReadableSize()); buffer_.Skip(nWritten); return nWritten != 0; } void Logger::_Reset() { curLevel_ = 0; pos_ = PREFIX_LEVEL_LEN + PREFIX_TIME_LEN ; } size_t Logger::_Log(const char* data, size_t dataLen) { const auto minLogSize = sizeof(int) + sizeof(size_t); size_t nOffset = 0; while (nOffset + minLogSize < dataLen) { int level = *(int*)(data + nOffset); size_t len = *(size_t* )(data + nOffset + sizeof(int)); if (dataLen < nOffset + minLogSize + len) { std::cerr << "_WriteLog skip 0!!!\n "; break; } _WriteLog(level, len, data + nOffset + minLogSize); nOffset += minLogSize + len; } return nOffset; } void Logger::_WriteLog(int level, size_t nLen, const char* data) { assert (nLen > 0 && data); if (dest_ & logConsole) { switch (level) { case logINFO: _Color(GREEN_COLOR); break; case logDEBUG: _Color(WHITE_COLOR); break; case logWARN: _Color(YELLOW_COLOR); break; case logERROR: _Color(RED_COLOR); break; case logUSR: _Color(PURPLE_COLOR); break; default: _Color(RED_COLOR); break; } fprintf(stdout, "%.*s", static_cast(nLen), data); _Color(NORMAL_COLOR); } if (dest_ & logFILE) { while (_CheckChangeFile()) { _CloseLogFile(); if (!_OpenLogFile(_MakeFileName().c_str())) { //OOPS!!! IMPOSSIBLE! break; } } assert (file_.IsOpen()); file_.Write(data, nLen); } } LogHelper::LogHelper(LogLevel level) : level_(level) { } Logger& LogHelper::operator=(Logger& log) { log.Flush(level_); return log; } LogManager& LogManager::Instance() { static LogManager mgr; return mgr; } LogManager::LogManager() { nullLog_.Init(0); logThread_.reset(new LogThread); } LogManager::~LogManager() { for (auto log : logs_) { delete log; } } Logger* LogManager::CreateLog(unsigned int level , unsigned int dest , const char* pDir) { Logger* pLog(new Logger); if (!pLog->Init(level, dest, pDir)) { delete pLog; return &nullLog_; } else { std::lock_guard guard(logsMutex_); logs_.insert(pLog); } return pLog; } bool LogManager::StartLog() { std::cout << "start log thread\n"; assert (!logThread_->IsAlive()); logThread_->SetAlive(); shutdown_ = false; ThreadPool::Instance().ExecuteTask(std::bind(&LogThread::Run, logThread_)); return true; } void LogManager::StopLog() { std::cout << "stop log thread\n"; shutdown_ = true; logThread_->Stop(); } bool LogManager::Update() { bool busy = false; std::lock_guard guard(logsMutex_); for (auto log : logs_) { if (log->Update() && !busy) { busy = true; } } return busy; } void LogManager::LogThread::Run() { while (IsAlive()) { if (!LogManager::Instance().Update()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } ================================================ FILE: QBase/Log/Logger.h ================================================ #ifndef BERT_LOGGER_H #define BERT_LOGGER_H #include #include #include #include "../Threads/ThreadPool.h" #include "../AsyncBuffer.h" #include "MemoryFile.h" #include "../Timer.h" enum LogLevel { logINFO = 0x01 << 0, logDEBUG = 0x01 << 1, logWARN = 0x01 << 2, logERROR = 0x01 << 3, logUSR = 0x01 << 4, logALL = 0xFFFFFFFF, }; enum LogDest { logConsole = 0x01 << 0, logFILE = 0x01 << 1, logSocket = 0x01 << 2, // TODO : LOG SERVER }; unsigned int ConvertLogLevel(const std::string& level); class Logger { public: friend class LogManager; Logger(const Logger& ) = delete; void operator= (const Logger& ) = delete; bool Init(unsigned int level = logDEBUG, unsigned int dest = logConsole, const char* pDir = 0); void Flush(LogLevel level); bool IsLevelForbid(unsigned int level) { return !(level & level_); }; Logger& operator<<(const char* msg); Logger& operator<<(const unsigned char* msg); Logger& operator<<(const std::string& msg); Logger& operator<<(void* ); Logger& operator<<(unsigned char a); Logger& operator<<(char a); Logger& operator<<(unsigned short a); Logger& operator<<(short a); Logger& operator<<(unsigned int a); Logger& operator<<(int a); Logger& operator<<(unsigned long a); Logger& operator<<(long a); Logger& operator<<(unsigned long long a); Logger& operator<<(long long a); Logger& operator<<(double a); Logger& SetCurLevel(unsigned int level) { curLevel_ = level; return *this; } bool Update(); private: Logger(); ~Logger(); static const size_t MAXLINE_LOG = 2048; // TODO char tmpBuffer_[MAXLINE_LOG]; std::size_t pos_; Time time_; unsigned int level_; std::string directory_; unsigned int dest_; std::string fileName_; unsigned int curLevel_; AsyncBuffer buffer_; OutputMemoryFile file_; // for optimization uint64_t lastLogSecond_; uint64_t lastLogMSecond_; std::size_t _Log(const char* data, std::size_t len); bool _CheckChangeFile(); const std::string& _MakeFileName(); bool _OpenLogFile(const char* name); void _CloseLogFile(); void _WriteLog(int level, std::size_t nLen, const char* data); void _Color(unsigned int color); void _Reset(); }; class LogHelper { public: LogHelper(LogLevel level); Logger& operator=(Logger& log); private: LogLevel level_; }; class LogManager { public: static LogManager& Instance(); ~LogManager(); LogManager(const LogManager& ) = delete; void operator=(const LogManager& ) = delete; Logger* CreateLog(unsigned int level = logDEBUG, unsigned int dest = logConsole, const char* pDir = 0); bool StartLog(); void StopLog(); bool Update(); Logger* NullLog() { return &nullLog_; } bool IsShutdown() const { return shutdown_; } private: LogManager(); Logger nullLog_; std::mutex logsMutex_; std::set logs_; class LogThread { public: LogThread() : alive_(false) {} void SetAlive() { alive_ = true; } bool IsAlive() const { return alive_; } void Stop() { alive_ = false; } void Run(); private: std::atomic alive_; }; std::shared_ptr logThread_; std::atomic shutdown_; }; extern __thread Logger* g_log; extern __thread unsigned g_logLevel; extern __thread unsigned g_logDest; #undef INF #undef DBG #undef WRN #undef ERR #undef USR #define LOG_DBG(x) (LogHelper(logDEBUG))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logDEBUG) #define LOG_INF(x) (LogHelper(logINFO))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logINFO) #define LOG_WRN(x) (LogHelper(logWARN))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logWARN) #define LOG_ERR(x) (LogHelper(logERROR))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logERROR) #define LOG_USR(x) (LogHelper(logUSR))=(x ? x : LogManager::Instance().NullLog())->SetCurLevel(logUSR) #define DBG LOG_DBG(g_log) #define INF LOG_INF(g_log) #define WRN LOG_WRN(g_log) #define ERR LOG_ERR(g_log) #define USR LOG_USR(g_log) #endif ================================================ FILE: QBase/Log/MemoryFile.cc ================================================ #include #include #include #include #include #include #include #include "MemoryFile.h" using std::size_t; static const size_t kDefaultSize = 1 * 1024 * 1024; static const int kInvalidFile = -1; static char* const kInvalidAddr = reinterpret_cast(-1); InputMemoryFile::InputMemoryFile() : file_(kInvalidFile), memory_(kInvalidAddr), offset_(0), size_(0) { } InputMemoryFile::~InputMemoryFile() { Close(); } bool InputMemoryFile::_MapReadOnly() { assert (file_ != kInvalidFile); assert (size_ == 0); struct stat st; fstat(file_, &st); size_ = st.st_size; memory_ = (char* )::mmap(0, size_, PROT_READ, MAP_PRIVATE, file_, 0); return memory_ != kInvalidAddr; } void InputMemoryFile::_CheckAvail(std::size_t len) { if (offset_ + len > size_) { std::string s("Not enough data, require " + std::to_string(len) + ", only has " + std::to_string(size_ - offset_)); throw std::runtime_error(std::move(s)); } } void InputMemoryFile::Attach(const char* data, size_t len) { memory_ = (char* )data; size_ = len; offset_ = 0; file_ = kInvalidFile; } bool InputMemoryFile::Open(const char* file) { Close(); file_ = ::open(file, O_RDONLY); if (file_ == kInvalidFile) { char err[128]; snprintf(err, sizeof err - 1, "OpenForRead %s failed\n", file); return false; } offset_ = 0; return _MapReadOnly(); } void InputMemoryFile::Close() { if (file_ != kInvalidFile) { ::munmap(memory_, size_); ::close(file_); file_ = kInvalidFile; size_ = 0; memory_ = kInvalidAddr; offset_ = 0; } } const char* InputMemoryFile::Read(std::size_t& len) { if (size_ <= offset_) return 0; if (offset_ + len > size_) len = size_ - offset_; return memory_ + offset_; } void InputMemoryFile::Skip(size_t len) { _CheckAvail(len); offset_ += len; } bool InputMemoryFile::IsOpen() const { return file_ != kInvalidFile; } // OutputMemoryFile OutputMemoryFile::OutputMemoryFile() : file_(kInvalidFile), memory_(kInvalidAddr), offset_(0), size_(0), syncPos_(0) { } OutputMemoryFile::~OutputMemoryFile() { Close(); } void OutputMemoryFile::_ExtendFileSize(size_t size) { assert(file_ != kInvalidFile); if (size > size_) Truncate(size); } bool OutputMemoryFile::Open(const std::string& file, bool bAppend) { return Open(file.c_str(), bAppend); } bool OutputMemoryFile::Open(const char* file, bool bAppend) { Close(); file_ = ::open(file, O_RDWR | O_CREAT | (bAppend ? O_APPEND : 0), 0644); if (file_ == kInvalidFile) { char err[128]; snprintf(err, sizeof err - 1, "OpenWriteOnly %s failed\n", file); perror(err); return false; } if (bAppend) { struct stat st; fstat(file_, &st); size_ = std::max(st.st_size, kDefaultSize); offset_ = st.st_size; } else { size_ = kDefaultSize; offset_ = 0; } ::ftruncate(file_, size_); return _MapWriteOnly(); } void OutputMemoryFile::Close() { if (file_ != kInvalidFile) { ::munmap(memory_, size_); ::ftruncate(file_, offset_); ::close(file_); file_ = kInvalidFile; size_ = 0; memory_ = kInvalidAddr; offset_ = 0; syncPos_ = 0; } } bool OutputMemoryFile::Sync() { if (file_ == kInvalidFile) return false; if (syncPos_ >= offset_) return false; ::msync(memory_ + syncPos_, offset_ - syncPos_, MS_SYNC); syncPos_ = offset_; return true; } bool OutputMemoryFile::_MapWriteOnly() { if (size_ == 0 || file_ == kInvalidFile) return false; #if 0 // codes below cause coredump when file size > 4MB if (m_memory != kInvalidAddr) ::munmap(m_memory, m_size); #endif memory_ = (char*)::mmap(0, size_, PROT_WRITE, MAP_SHARED, file_, 0); return (memory_ != kInvalidAddr); } void OutputMemoryFile::Truncate(std::size_t size) { if (size == size_) return; size_ = size; ::ftruncate(file_, size); if (offset_> size_) offset_ = size_; _MapWriteOnly(); } void OutputMemoryFile::TruncateTailZero() { if (file_ == kInvalidFile) return; size_t tail = size_; while (tail > 0 && memory_[--tail] == '\0') ; ++ tail; Truncate(tail); } bool OutputMemoryFile::IsOpen() const { return file_ != kInvalidFile; } // consumer void OutputMemoryFile::Write(const void* data, size_t len) { _AssureSpace(len); assert(memory_); ::memcpy(memory_ + offset_, data, len); offset_ += len; assert(offset_ <= size_); } void OutputMemoryFile::_AssureSpace(size_t size) { size_t newSize = size_; while (offset_ + size > newSize) { if (newSize == 0) newSize = kDefaultSize; else newSize <<= 1; } _ExtendFileSize(newSize); } ================================================ FILE: QBase/Log/MemoryFile.h ================================================ #ifndef BERT_MEMORYFILE_H #define BERT_MEMORYFILE_H #include #include class InputMemoryFile { public: InputMemoryFile(); ~InputMemoryFile(); bool Open(const char* file); void Close(); void Attach(const char* data, size_t len); const char* Read(std::size_t& len); template T Read(); void Skip(std::size_t len); bool IsOpen() const; private: bool _MapReadOnly(); void _CheckAvail(std::size_t len); int file_; char* memory_; std::size_t offset_; std::size_t size_; }; template inline T InputMemoryFile::Read() { _CheckAvail(sizeof(T)); T res(*reinterpret_cast(memory_ + offset_)); offset_ += sizeof(T); return res; } class OutputMemoryFile { public: OutputMemoryFile(); ~OutputMemoryFile(); bool Open(const std::string& file, bool bAppend = true); bool Open(const char* file, bool bAppend = true); void Close(); bool Sync(); void Truncate(std::size_t size); //!! if process terminated abnormally, erase the trash data //requirement: text file. void TruncateTailZero(); void Write(const void* data, std::size_t len); template void Write(const T& t); std::size_t Offset() const { return offset_; } bool IsOpen() const; private: bool _MapWriteOnly(); void _ExtendFileSize(std::size_t size); void _AssureSpace(std::size_t size); int file_; char* memory_; std::size_t offset_; std::size_t size_; std::size_t syncPos_; }; template inline void OutputMemoryFile::Write(const T& t) { this->Write(&t, sizeof t); } #endif ================================================ FILE: QBase/NetThreadPool.cc ================================================ #include "NetThreadPool.h" #include "StreamSocket.h" #include "Log/Logger.h" #include "Timer.h" #if defined(__gnu_linux__) #include "EPoller.h" #elif defined(__APPLE__) #include "Kqueue.h" #else #error "Only support osx and linux" #endif #include #include namespace Internal { NetThread::NetThread() : running_(true), newCnt_(0) { #if defined(__gnu_linux__) poller_.reset(new Epoller); #else poller_.reset(new Kqueue); #endif } NetThread::~NetThread() { } void NetThread::AddSocket(PSOCKET task, uint32_t events) { std::lock_guard guard(mutex_); newTasks_.push_back(std::make_pair(task, events)); ++ newCnt_; assert (newCnt_ == static_cast(newTasks_.size())); } void NetThread::ModSocket(PSOCKET task, uint32_t events) { poller_->ModSocket(task->GetSocket(), events, task.get()); } void NetThread::RemoveSocket(PSOCKET task, uint32_t events) { poller_->DelSocket(task->GetSocket(), events); } void NetThread::_TryAddNewTasks() { if (newCnt_ > 0 && mutex_.try_lock()) { NewTasks tmp; newTasks_.swap(tmp); newCnt_ = 0; mutex_.unlock(); auto iter(tmp.begin()), end (tmp.end()); for (; iter != end; ++ iter) _AddSocket(iter->first, iter->second); } } void NetThread::_AddSocket(PSOCKET task, uint32_t events) { if (poller_->AddSocket(task->GetSocket(), events, task.get())) tasks_.push_back(task); } ////////////////////////////////// void RecvThread::Run() { // init log; g_logLevel = logALL; g_logDest = logFILE; if (g_logLevel && g_logDest) { g_log = LogManager::Instance().CreateLog(g_logLevel, g_logDest, "recvthread_log"); } std::deque::iterator it; int loopCount = 0; while (IsAlive()) { _TryAddNewTasks(); if (tasks_.empty()) { std::this_thread::sleep_for(std::chrono::microseconds(100)); continue; } const int nReady = poller_->Poll(firedEvents_, static_cast(tasks_.size()), 1); for (int i = 0; i < nReady; ++ i) { assert (!(firedEvents_[i].events & EventTypeWrite)); Socket* sock = (Socket* )firedEvents_[i].userdata; if (firedEvents_[i].events & EventTypeRead) { if (!sock->OnReadable()) { sock->OnError(); } } if (firedEvents_[i].events & EventTypeError) { sock->OnError(); } } if (nReady == 0) loopCount *= 2; if (++ loopCount < 100000) continue; loopCount = 0; for (auto it(tasks_.begin()); it != tasks_.end(); ) { if ((*it)->Invalid()) { NetThreadPool::Instance().DisableRead(*it); RemoveSocket(*it, EventTypeRead); it = tasks_.erase(it); } else { ++ it; } } } } void SendThread::Run( ) { // init log; g_logLevel = logALL; g_logDest = logFILE; if (g_logLevel && g_logDest) { g_log = LogManager::Instance().CreateLog(g_logLevel, g_logDest, "sendthread_log"); } std::deque::iterator it; while (IsAlive()) { _TryAddNewTasks(); size_t nOut = 0; for (it = tasks_.begin(); it != tasks_.end(); ) { Socket::SocketType type = (*it)->GetSocketType(); Socket* sock = (*it).get(); if (type == Socket::SocketType_Stream) { StreamSocket* tcpSock = static_cast(sock); if (!tcpSock->Send()) tcpSock->OnError(); } if (sock->Invalid()) { NetThreadPool::Instance().DisableWrite(*it); RemoveSocket(*it, EventTypeWrite); it = tasks_.erase(it); } else { if (sock->epollOut_) ++ nOut; ++ it; } } if (nOut == 0) { std::this_thread::sleep_for(std::chrono::microseconds(100)); continue; } const int nReady = poller_->Poll(firedEvents_, static_cast(tasks_.size()), 1); for (int i = 0; i < nReady; ++ i) { Socket* sock = (Socket* )firedEvents_[i].userdata; assert (!(firedEvents_[i].events & EventTypeRead)); if (firedEvents_[i].events & EventTypeWrite) { if (!sock->OnWritable()) { sock->OnError(); } } if (firedEvents_[i].events & EventTypeError) { sock->OnError(); } } } } void NetThreadPool::StopAllThreads() { recvThread_->Stop(); recvThread_.reset(); sendThread_->Stop(); sendThread_.reset(); INF << "Stop all recv and send threads"; } bool NetThreadPool::AddSocket(PSOCKET sock, uint32_t events) { if (events & EventTypeRead) { if (!recvThread_) return false; recvThread_->AddSocket(sock, EventTypeRead); } if (events & EventTypeWrite) { if (!sendThread_) return false; sendThread_->AddSocket(sock, EventTypeWrite); } return true; } bool NetThreadPool::StartAllThreads() { recvThread_.reset(new RecvThread); sendThread_.reset(new SendThread); ThreadPool::Instance().ExecuteTask(std::bind(&RecvThread::Run, recvThread_)); ThreadPool::Instance().ExecuteTask(std::bind(&SendThread::Run, sendThread_)); return true; } void NetThreadPool::EnableRead(const std::shared_ptr& sock) { if (recvThread_) recvThread_->ModSocket(sock, EventTypeRead); } void NetThreadPool::EnableWrite(const std::shared_ptr& sock) { if (sendThread_) sendThread_->ModSocket(sock, EventTypeWrite); } void NetThreadPool::DisableRead(const std::shared_ptr& sock) { if (recvThread_) recvThread_->ModSocket(sock, 0); } void NetThreadPool::DisableWrite(const std::shared_ptr& sock) { if (sendThread_) sendThread_->ModSocket(sock, 0); } } ================================================ FILE: QBase/NetThreadPool.h ================================================ #ifndef BERT_NETTHREADPOOL_H #define BERT_NETTHREADPOOL_H #include #include #include #include #include #include #include "Poller.h" #include "Threads/ThreadPool.h" inline long GetCpuNum() { return sysconf(_SC_NPROCESSORS_ONLN); } class Socket; typedef std::shared_ptr PSOCKET; namespace Internal { class NetThread { public: NetThread(); virtual ~NetThread(); bool IsAlive() const { return running_; } void Stop() { running_ = false;} void AddSocket(PSOCKET , uint32_t event); void ModSocket(PSOCKET , uint32_t event); void RemoveSocket(PSOCKET, uint32_t event); protected: std::unique_ptr poller_; std::vector firedEvents_; std::deque tasks_; void _TryAddNewTasks(); private: std::atomic running_; std::mutex mutex_; typedef std::vector, uint32_t> > NewTasks; NewTasks newTasks_; std::atomic newCnt_; void _AddSocket(PSOCKET , uint32_t event); }; class RecvThread : public NetThread { public: void Run(); }; class SendThread : public NetThread { public: void Run(); }; /////////////////////////////////////////////// class NetThreadPool { std::shared_ptr recvThread_; std::shared_ptr sendThread_; public: NetThreadPool() = default; NetThreadPool(const NetThreadPool& ) = delete; void operator= (const NetThreadPool& ) = delete; bool AddSocket(PSOCKET , uint32_t event); bool StartAllThreads(); void StopAllThreads(); void EnableRead(const std::shared_ptr& sock); void EnableWrite(const std::shared_ptr& sock); void DisableRead(const std::shared_ptr& sock); void DisableWrite(const std::shared_ptr& sock); static NetThreadPool& Instance() { static NetThreadPool pool; return pool; } }; } #endif ================================================ FILE: QBase/Poller.h ================================================ #ifndef BERT_POLLER_H #define BERT_POLLER_H #include enum EventType { EventTypeRead = 0x1, EventTypeWrite = 0x1 << 1, EventTypeError = 0x1 << 2, }; struct FiredEvent { int events; void* userdata; FiredEvent() : events(0), userdata(0) { } }; class Poller { public: Poller() : multiplexer_(-1) { } virtual ~Poller() { } virtual bool AddSocket(int sock, int events, void* userPtr) = 0; virtual bool ModSocket(int sock, int events, void* userPtr) = 0; virtual bool DelSocket(int sock, int events) = 0; virtual int Poll(std::vector& events, std::size_t maxEv, int timeoutMs) = 0; protected: int multiplexer_; }; #endif ================================================ FILE: QBase/README.md ================================================ # QBase QBase最初是C++03代码,由于历史原因,再加上它也极其稳定,我不打算替换了。 感兴趣的可以看我的另外一个最新后台开发基础库[ananas](https://github.com/loveyacper/ananas) ================================================ FILE: QBase/Server.cc ================================================ #include #include #include #include #include #include "Server.h" #include "ListenSocket.h" #include "ClientSocket.h" #include "StreamSocket.h" #include "NetThreadPool.h" #include "Timer.h" #include "Log/Logger.h" using Internal::NetThreadPool; void Server::IntHandler(int signum) { if (Server::Instance() != NULL) Server::Instance()->Terminate(); } void Server::HupHandler(int signum) { if (Server::Instance() != NULL) Server::Instance()->reloadCfg_ = true; } Server* Server::sinstance_ = nullptr; std::set Server::slistenSocks_; Server::Server() : bTerminate_(false), reloadCfg_(false) { if (sinstance_ == NULL) sinstance_ = this; else ::abort(); } Server::~Server() { sinstance_ = NULL; } bool Server::_RunLogic() { return tasks_.DoMsgParse(); } bool Server::TCPBind(const SocketAddr& addr, int tag) { using Internal::ListenSocket; auto s(std::make_shared(tag)); if (s->Bind(addr)) { slistenSocks_.insert(s->GetSocket()); return true; } return false; } void Server::TCPReconnect(const SocketAddr& peer, int tag) { INF << __FUNCTION__ << peer.GetIP(); Timer* pTimer = TimerManager::Instance().CreateTimer(); pTimer->Init(2 * 1000, 1); pTimer->SetCallback([&, tag, peer]() { USR << "OnTimer reconnect to " << peer.GetIP() << ":" << peer.GetPort(); Server::Instance()->TCPConnect(peer, [=]() { Server::Instance()->TCPReconnect(peer, tag); }, tag); }); TimerManager::Instance().AsyncAddTimer(pTimer); } void Server::TCPConnect(const SocketAddr& peer, int tag) { _TCPConnect(peer, nullptr, tag); } void Server::TCPConnect(const SocketAddr& peer, const std::function& cb, int tag) { _TCPConnect(peer, &cb, tag); } void Server::_TCPConnect(const SocketAddr& peer, const std::function* cb, int tag) { auto client(std::make_shared(tag)); if (cb) client->SetFailCallback(*cb); client->Connect(peer); } void Server::MainLoop(bool daemon) { struct sigaction sig; ::memset(&sig, 0, sizeof(sig)); sig.sa_handler = &Server::IntHandler; sigaction(SIGINT, &sig, NULL); sigaction(SIGQUIT, &sig, NULL); sigaction(SIGABRT, &sig, NULL); sigaction(SIGTERM, &sig, NULL); sig.sa_handler = &Server::HupHandler; sigaction(SIGHUP, &sig, NULL); sig.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig, NULL); ::pthread_atfork(nullptr, nullptr, AtForkHandler); ::srand(static_cast(time(NULL))); ::srandom(static_cast(time(NULL))); // daemon must be first, before descriptor open, threads create if (daemon) { ::daemon(1, 0); } if (NetThreadPool::Instance().StartAllThreads() && _Init() && LogManager::Instance().StartLog()) { while (!bTerminate_) { if (reloadCfg_) { ReloadConfig(); reloadCfg_ = false; } if (!_RunLogic()) std::this_thread::sleep_for(std::chrono::microseconds(100)); } } tasks_.Clear(); _Recycle(); NetThreadPool::Instance().StopAllThreads(); LogManager::Instance().StopLog(); ThreadPool::Instance().JoinAll(); } std::shared_ptr Server::_OnNewConnection(int tcpsock, int tag) { WRN << "implement your tcp accept, now close socket " << tcpsock; return std::shared_ptr(nullptr); } void Server::NewConnection(int sock, int tag, const std::function& cb) { if (sock == INVALID_SOCKET) return; auto conn = _OnNewConnection(sock, tag); if (!conn) { Socket::CloseSocket(sock); return; } conn->SetOnDisconnect(cb); if (NetThreadPool::Instance().AddSocket(conn, EventTypeRead | EventTypeWrite)) tasks_.AddTask(conn); } void Server::AtForkHandler() { for (auto sock : slistenSocks_) { close(sock); } } void Server::DelListenSock(int sock) { if (sock == INVALID_SOCKET) return; auto n = slistenSocks_.erase(sock); if (n != 1) ERR << "Failed DelListenSock " << sock; else INF << "Success DelListenSock " << sock; } ================================================ FILE: QBase/Server.h ================================================ #ifndef BERT_SERVER_H #define BERT_SERVER_H #include #include #include "TaskManager.h" struct SocketAddr; class Server // Should singleton { protected: virtual bool _RunLogic(); virtual void _Recycle() { } virtual bool _Init() = 0; Server(); Server(const Server& ) = delete; void operator= (const Server& ) = delete; public: virtual ~Server(); bool TCPBind(const SocketAddr& listenAddr, int tag); void TCPReconnect(const SocketAddr& peer, int tag); static Server* Instance() { return sinstance_; } bool IsTerminate() const { return bTerminate_; } void Terminate() { bTerminate_ = true; } void MainLoop(bool daemon = false); void NewConnection(int sock, int tag, const std::function& cb = std::function()); void TCPConnect(const SocketAddr& peer, int tag); void TCPConnect(const SocketAddr& peer, const std::function& cb, int tag); size_t TCPSize() const { return tasks_.TCPSize(); } // SIGHUP handler, in fact, you should load config use this function; virtual void ReloadConfig() { } static void IntHandler(int sig); static void HupHandler(int sig); std::shared_ptr FindTCP(unsigned int id) const { return tasks_.FindTCP(id); } static void AtForkHandler(); static void DelListenSock(int sock); private: virtual std::shared_ptr _OnNewConnection(int tcpsock, int tag); void _TCPConnect(const SocketAddr& peer, const std::function* cb = nullptr, int tag = -1); std::atomic bTerminate_; Internal::TaskManager tasks_; bool reloadCfg_; static Server* sinstance_; static std::set slistenSocks_; }; #endif ================================================ FILE: QBase/Socket.cc ================================================ #include #include #include #include #include #include #include #if defined(__APPLE__) #include #endif #include "Socket.h" #include "NetThreadPool.h" #include "Log/Logger.h" std::atomic Socket::sid_; Socket::Socket() : localSock_(INVALID_SOCKET), epollOut_(false), invalid_(false) { ++ sid_; std::size_t expect = 0; sid_.compare_exchange_strong(expect, 1); id_ = sid_; } Socket::~Socket() { CloseSocket(localSock_); } int Socket::CreateUDPSocket() { return ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } bool Socket::OnError() { bool expect = false; if (invalid_.compare_exchange_strong(expect, true)) { return true; } return false; } int Socket::CreateTCPSocket() { return ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } void Socket::CloseSocket(int& sock) { if (sock != INVALID_SOCKET) { ::shutdown(sock, SHUT_RDWR); ::close(sock); DBG << "CloseSocket " << sock; sock = INVALID_SOCKET; } } void Socket::SetNonBlock(int sock, bool nonblock) { int flag = ::fcntl(sock, F_GETFL, 0); assert(flag >= 0 && "Non Block failed"); if (nonblock) flag = ::fcntl(sock, F_SETFL, flag | O_NONBLOCK); else flag = ::fcntl(sock, F_SETFL, flag & ~O_NONBLOCK); } void Socket::SetNodelay(int sock) { int nodelay = 1; ::setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(int)); } void Socket::SetSndBuf(int sock, socklen_t winsize) { ::setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&winsize, sizeof(winsize)); } void Socket::SetRcvBuf(int sock, socklen_t winsize) { ::setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char*)&winsize, sizeof(winsize)); } void Socket::SetReuseAddr(int sock) { int reuse = 1; ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)); } bool Socket::GetLocalAddr(int sock, SocketAddr& addr) { sockaddr_in localAddr; socklen_t len = sizeof(localAddr); if (0 == ::getsockname(sock, (struct sockaddr*)&localAddr, &len)) { addr.Init(localAddr); } else { return false; } return true; } bool Socket::GetPeerAddr(int sock, SocketAddr& addr) { sockaddr_in remoteAddr; socklen_t len = sizeof(remoteAddr); if (0 == ::getpeername(sock, (struct sockaddr*)&remoteAddr, &len)) { addr.Init(remoteAddr); } else { return false; } return true; } void Socket::GetMyAddrInfo(unsigned int* addrs, int num) { char buff[BUFSIZ]; struct ifconf conf; conf.ifc_len = BUFSIZ; conf.ifc_buf = buff; int sock = CreateUDPSocket(); ioctl(sock, SIOCGIFCONF, &conf); int maxnum = conf.ifc_len / sizeof(struct ifreq); struct ifreq* ifr = conf.ifc_req; int cnt = 0; for(int i=0; cnt < num && i < maxnum;i++) { if (NULL == ifr) break; ioctl(sock, SIOCGIFFLAGS, ifr); if(((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP)) { struct sockaddr_in *pAddr = (struct sockaddr_in *)(&ifr->ifr_addr); addrs[cnt ++] = pAddr->sin_addr.s_addr; } ++ ifr; } for ( ; cnt < num; ++ cnt) { addrs[cnt] = 0; } close(sock); } ================================================ FILE: QBase/Socket.h ================================================ #ifndef BERT_SOCKET_H #define BERT_SOCKET_H #include #include #include #include #include #include #define INVALID_SOCKET (int)(~0) #define SOCKET_ERROR (-1) #define INVALID_PORT (-1) struct SocketAddr { SocketAddr() { Clear(); } SocketAddr(const SocketAddr& other) { memcpy(&addr_, &other.addr_, sizeof addr_); } SocketAddr& operator= (const SocketAddr& other) { if (this != &other) memcpy(&addr_, &other.addr_, sizeof addr_); return *this; } SocketAddr(const sockaddr_in& addr) { Init(addr); } // ip port format: 127.0.0.1:6379 SocketAddr(const std::string& ipport) { std::string::size_type p = ipport.find_first_of(':'); std::string ip = ipport.substr(0, p); std::string port = ipport.substr(p + 1); Init(ip.c_str(), static_cast(std::stoi(port))); } SocketAddr(uint32_t netip, uint16_t netport) { Init(netip, netport); } SocketAddr(const char* ip, uint16_t hostport) { Init(ip, hostport); } void Init(const sockaddr_in& addr) { memcpy(&addr_, &addr, sizeof(addr)); } void Init(uint32_t netip, uint16_t netport) { addr_.sin_family = AF_INET; addr_.sin_addr.s_addr = netip; addr_.sin_port = netport; } void Init(const char* ip, uint16_t hostport) { addr_.sin_family = AF_INET; addr_.sin_addr.s_addr = ::inet_addr(ip); addr_.sin_port = htons(hostport); } const sockaddr_in& GetAddr() const { return addr_; } const char* GetIP() const { return ::inet_ntoa(addr_.sin_addr); } const char* GetIP(char* buf, socklen_t size) const { return ::inet_ntop(AF_INET, (const char*)&addr_.sin_addr, buf, size); } unsigned short GetPort() const { return ntohs(addr_.sin_port); } std::string ToString() const { char tmp[32]; const char* res = inet_ntop(AF_INET, &addr_.sin_addr, tmp, (socklen_t)(sizeof tmp)); return std::string(res) + ":" + std::to_string(ntohs(addr_.sin_port)); } bool Empty() const { return 0 == addr_.sin_family; } void Clear() { memset(&addr_, 0, sizeof addr_); } inline friend bool operator== (const SocketAddr& a, const SocketAddr& b) { return a.addr_.sin_family == b.addr_.sin_family && a.addr_.sin_addr.s_addr == b.addr_.sin_addr.s_addr && a.addr_.sin_port == b.addr_.sin_port ; } inline friend bool operator!= (const SocketAddr& a, const SocketAddr& b) { return !(a == b); } sockaddr_in addr_; }; // custom specialization of std::hash can be injected into std namespace std { template<> struct hash { typedef SocketAddr argument_type; typedef std::size_t result_type; result_type operator()(const argument_type& s) const noexcept { result_type h1 = std::hash{}(s.addr_.sin_family); result_type h2 = std::hash{}(s.addr_.sin_port); result_type h3 = std::hash{}(s.addr_.sin_addr.s_addr); result_type tmp = h1 ^ (h2 << 1); return h3 ^ (tmp << 1); } }; } namespace Internal { class SendThread; } // Abstraction for a TCP socket class Socket : public std::enable_shared_from_this { friend class Internal::SendThread; public: virtual ~Socket(); Socket(const Socket& ) = delete; void operator= (const Socket& ) = delete; enum SocketType { SocketType_Invalid= -1, SocketType_Listen, SocketType_Client, SocketType_Stream, }; virtual SocketType GetSocketType() const { return SocketType_Invalid; } bool Invalid() const { return invalid_; } int GetSocket() const { return localSock_; } std::size_t GetID() const { return id_; } virtual bool OnReadable() { return false; } virtual bool OnWritable() { return false; } virtual bool OnError(); virtual void OnConnect() { } virtual void OnDisconnect() { } static int CreateTCPSocket(); static int CreateUDPSocket(); static void CloseSocket(int &sock); static void SetNonBlock(int sock, bool nonBlock = true); static void SetNodelay(int sock); static void SetSndBuf(int sock, socklen_t size = 128 * 1024); static void SetRcvBuf(int sock, socklen_t size = 128 * 1024); static void SetReuseAddr(int sock); static bool GetLocalAddr(int sock, SocketAddr& ); static bool GetPeerAddr(int sock, SocketAddr& ); static void GetMyAddrInfo(unsigned int* addrs, int num); protected: Socket(); // The local socket int localSock_; bool epollOut_; private: std::atomic invalid_; std::size_t id_; static std::atomic sid_; }; #endif ================================================ FILE: QBase/StreamSocket.cc ================================================ #include #include #include #include #include "StreamSocket.h" #include "Server.h" #include "NetThreadPool.h" #include "Log/Logger.h" using std::size_t; StreamSocket::StreamSocket() { } StreamSocket::~StreamSocket() { INF << __FUNCTION__ << " peer (" << peerAddr_.ToString() << ")"; } bool StreamSocket::Init(int fd, const SocketAddr& peer) { if (fd < 0) return false; peerAddr_ = peer; localSock_ = fd; SetNonBlock(localSock_); INF << __FUNCTION__ << " peer address (" << peerAddr_.ToString() << "), local socket is " << fd; #if defined(__APPLE__) int set = 1; setsockopt(localSock_, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); #endif return true; } int StreamSocket::Recv() { if (recvBuf_.Capacity() == 0) { recvBuf_.InitCapacity(64 * 1024); // First recv data, allocate buffer } BufferSequence buffers; recvBuf_.GetSpace(buffers); if (buffers.count == 0) { WRN << "Recv buffer is full"; return 0; } int ret = static_cast(::readv(localSock_, buffers.buffers, static_cast(buffers.count))); if (ret == ERRORSOCKET && (EAGAIN == errno || EWOULDBLOCK == errno)) return 0; if (ret > 0) recvBuf_.AdjustWritePtr(ret); return (0 == ret) ? EOFSOCKET : ret; } int StreamSocket::_Send(const BufferSequence& bf) { auto total = bf.TotalBytes(); if (total == 0) return 0; int ret = static_cast(::writev(localSock_, bf.buffers, static_cast(bf.count))); if (ERRORSOCKET == ret && (EAGAIN == errno || EWOULDBLOCK == errno)) { epollOut_ = true; ret = 0; } else if (ret > 0 && static_cast(ret) < total) { epollOut_ = true; } else if (static_cast(ret) == total) { epollOut_ = false; } return ret; } bool StreamSocket::SendPacket(const void* data, size_t bytes) { if (data && bytes > 0) sendBuf_.Write(data, bytes); return true; } bool StreamSocket::SendPacket(Buffer& bf) { return SendPacket(bf.ReadAddr(), bf.ReadableSize()); } bool StreamSocket::SendPacket(AttachedBuffer& af) { return SendPacket(af.ReadAddr(), af.ReadableSize()); } bool StreamSocket::SendPacket(qedis::UnboundedBuffer& ubf) { return SendPacket(ubf.ReadAddr(), ubf.ReadableSize()); } bool StreamSocket::OnReadable() { int nBytes = StreamSocket::Recv(); if (nBytes < 0) { INF << __FUNCTION__ << " failed, peer address (" << peerAddr_.ToString() << "), local socket is " << localSock_; Internal::NetThreadPool::Instance().DisableRead(shared_from_this()); return false; } return true; } bool StreamSocket::Send() { if (epollOut_) return true; BufferSequence bf; sendBuf_.ProcessBuffer(bf); size_t total = bf.TotalBytes(); if (total == 0) return true; int nSent = _Send(bf); if (nSent > 0) { sendBuf_.Skip(nSent); } if (epollOut_) { Internal::NetThreadPool::Instance().EnableWrite(shared_from_this()); INF << __FUNCTION__ << " peer address (" << peerAddr_.ToString() << "), local socket is " << localSock_ << ", register write event"; } return nSent >= 0; } // drive by EPOLLOUT bool StreamSocket::OnWritable() { BufferSequence bf; sendBuf_.ProcessBuffer(bf); size_t total = bf.TotalBytes(); int nSent = 0; if (total > 0) { nSent = _Send(bf); if (nSent > 0) sendBuf_.Skip(nSent); } else { epollOut_ = false; } if (!epollOut_) { INF << __FUNCTION__ << " peer address (" << peerAddr_.ToString() << "), local socket is " << localSock_ << ", unregister write event"; Internal::NetThreadPool::Instance().DisableWrite(shared_from_this()); } return nSent >= 0; } bool StreamSocket::OnError() { if (Socket::OnError()) { ERR << __FUNCTION__ << " peer address (" << peerAddr_.ToString() << "), local socket is " << localSock_; if (onDisconnect_) onDisconnect_(); return true; } return false; } bool StreamSocket::DoMsgParse() { bool busy = false; while (!recvBuf_.IsEmpty()) { BufferSequence datum; recvBuf_.GetDatum(datum, recvBuf_.ReadableSize()); AttachedBuffer af(datum); auto bodyLen = _HandlePacket(af.ReadAddr(), af.ReadableSize()); if (bodyLen > 0) { busy = true; recvBuf_.AdjustReadPtr(bodyLen); } else { break; } } return busy; } ================================================ FILE: QBase/StreamSocket.h ================================================ #ifndef BERT_STREAMSOCKET_H #define BERT_STREAMSOCKET_H #include "AsyncBuffer.h" #include "Socket.h" #include #include using PacketLength = int32_t; // Abstraction for a TCP connection class StreamSocket : public Socket { friend class SendThread; public: StreamSocket(); ~StreamSocket(); bool Init(int localfd, const SocketAddr& peer); SocketType GetSocketType() const { return SocketType_Stream; } public: // Receive data int Recv(); public: // Send data bool SendPacket(const void* , std::size_t ); bool SendPacket(Buffer& bf); bool SendPacket(AttachedBuffer& abf); bool SendPacket(qedis::UnboundedBuffer& ubf); template bool SendPacket(StackBuffer& sb); bool OnReadable(); bool OnWritable(); bool OnError(); bool DoMsgParse(); // false if no msg void SetOnDisconnect(const std::function& cb = std::function()) { onDisconnect_ = cb; } // send thread bool Send(); const SocketAddr& GetPeerAddr() const { return peerAddr_; } protected: SocketAddr peerAddr_; private: std::function onDisconnect_; int _Send(const BufferSequence& bf); virtual PacketLength _HandlePacket(const char* msg, std::size_t len) = 0; // For human readability enum { TIMEOUTSOCKET = 0, ERRORSOCKET = -1, EOFSOCKET = -2, }; Buffer recvBuf_; AsyncBuffer sendBuf_; }; template inline bool StreamSocket::SendPacket(StackBuffer& sf) { return SendPacket(sf.ReadAddr(), sf.ReadableSize()); } #endif ================================================ FILE: QBase/TaskManager.cc ================================================ #include #include "TaskManager.h" #include "StreamSocket.h" #include "Log/Logger.h" namespace Internal { TaskManager::~TaskManager() { assert(Empty() && "Why you do not clear container before exit?"); } bool TaskManager::AddTask(PTCPSOCKET task) { std::lock_guard guard(lock_); newTasks_.push_back(task); ++ newCnt_; return true; } TaskManager::PTCPSOCKET TaskManager::FindTCP(unsigned int id) const { if (id > 0) { auto it = tcpSockets_.find(id); if (it != tcpSockets_.end()) return it->second; } return PTCPSOCKET(); } bool TaskManager::_AddTask(PTCPSOCKET task) { //bool succ = tcpSockets_.insert(std::map::value_type(task->GetID(), task)).second; bool succ = tcpSockets_.insert({task->GetID(), task}).second; return succ; } void TaskManager::_RemoveTask(std::map::iterator& it) { tcpSockets_.erase(it ++); } bool TaskManager::DoMsgParse() { if (newCnt_ > 0 && lock_.try_lock()) { NEWTASKS_T tmpNewTask; tmpNewTask.swap(newTasks_); newCnt_ = 0; lock_.unlock(); for (const auto& task : tmpNewTask) { if (!_AddTask(task)) { ERR << "Why can not insert tcp socket " << task->GetSocket() << ", id = " << task->GetID(); } else { INF << "New connection from " << task->GetPeerAddr().ToString() << ", id = " << task->GetID(); task->OnConnect(); } } } bool busy = false; for (auto it(tcpSockets_.begin()); it != tcpSockets_.end(); ) { if (!it->second || it->second->Invalid()) { if (it->second) { INF << "Close connection from " << it->second->GetPeerAddr().ToString() << ", id = " << it->second->GetID(); it->second->OnDisconnect(); } _RemoveTask(it); } else { if (it->second->DoMsgParse() && !busy) busy = true; ++ it; } } return busy; } } ================================================ FILE: QBase/TaskManager.h ================================================ #ifndef BERT_TASKMANAGER_H #define BERT_TASKMANAGER_H #include #include #include #include #include class StreamSocket; namespace Internal { class TaskManager { typedef std::shared_ptr PTCPSOCKET; typedef std::vector NEWTASKS_T; public: TaskManager() : newCnt_(0) { } ~TaskManager(); bool AddTask(PTCPSOCKET ); bool Empty() const { return tcpSockets_.empty(); } void Clear() { tcpSockets_.clear(); } PTCPSOCKET FindTCP(unsigned int id) const; size_t TCPSize() const { return tcpSockets_.size(); } bool DoMsgParse(); private: bool _AddTask(PTCPSOCKET task); void _RemoveTask(std::map::iterator& ); std::map tcpSockets_; // Lock for new tasks std::mutex lock_; NEWTASKS_T newTasks_; std::atomic newCnt_; // vector::empty() is not thread-safe !!! }; } #endif ================================================ FILE: QBase/Threads/ThreadPool.cc ================================================ #include "ThreadPool.h" __thread bool ThreadPool::working_ = true; ThreadPool::ThreadPool() : waiters_(0), shutdown_(false) { monitor_ = std::thread([this]() { this->_MonitorRoutine(); } ); maxIdleThread_ = std::max(1U, std::thread::hardware_concurrency()); pendingStopSignal_ = 0; } ThreadPool::~ThreadPool() { JoinAll(); } ThreadPool& ThreadPool::Instance() { static ThreadPool pool; return pool; } void ThreadPool::SetMaxIdleThread(unsigned int m) { if (0 < m && m <= kMaxThreads) maxIdleThread_ = m; } void ThreadPool::JoinAll() { decltype(workers_) tmp; { std::unique_lock guard(mutex_); if (shutdown_) return; shutdown_ = true; cond_.notify_all(); tmp.swap(workers_); workers_.clear(); } for (auto& t : tmp) { if (t.joinable()) t.join(); } if (monitor_.joinable()) monitor_.join(); } void ThreadPool::_CreateWorker() { std::thread t([this]() { this->_WorkerRoutine(); } ); workers_.push_back(std::move(t)); } void ThreadPool::_WorkerRoutine() { working_ = true; while (working_) { std::function task; { std::unique_lock guard(mutex_); ++ waiters_; cond_.wait(guard, [this]()->bool { return this->shutdown_ || !tasks_.empty(); } ); -- waiters_; if (this->shutdown_ && tasks_.empty()) return; task = std::move(tasks_.front()); tasks_.pop_front(); } task(); } // if reach here, this thread is recycled by monitor thread -- pendingStopSignal_; } void ThreadPool::_MonitorRoutine() { while (!shutdown_) { std::this_thread::sleep_for(std::chrono::seconds(1)); std::unique_lock guard(mutex_); if (shutdown_) return; auto nw = waiters_; // if there is any pending stop signal to consume waiters nw -= pendingStopSignal_; while (nw -- > maxIdleThread_) { tasks_.push_back([this]() { working_ = false; }); cond_.notify_one(); ++ pendingStopSignal_; } } } ================================================ FILE: QBase/Threads/ThreadPool.h ================================================ #ifndef BERT_THREADPOOL_H #define BERT_THREADPOOL_H #include #include #include #include #include #include #include #include class ThreadPool final { public: ~ThreadPool(); ThreadPool(const ThreadPool& ) = delete; void operator=(const ThreadPool& ) = delete; static ThreadPool& Instance(); template auto ExecuteTask(F&& f, Args&&... args) -> std::future::type>; void JoinAll(); void SetMaxIdleThread(unsigned int m); private: ThreadPool(); void _CreateWorker(); void _WorkerRoutine(); void _MonitorRoutine(); std::thread monitor_; std::atomic maxIdleThread_; std::atomic pendingStopSignal_; static __thread bool working_; std::deque workers_; std::mutex mutex_; std::condition_variable cond_; unsigned waiters_; bool shutdown_; std::deque > tasks_; static const int kMaxThreads = 256; }; template auto ThreadPool::ExecuteTask(F&& f, Args&&... args) -> std::future::type> { using resultType = typename std::result_of::type; auto task = std::make_shared >(std::bind(std::forward(f), std::forward(args)...)); { std::unique_lock guard(mutex_); if (shutdown_) { return std::future(); } tasks_.emplace_back( [=]() { (*task)(); } ); if (waiters_ == 0) { _CreateWorker(); } cond_.notify_one(); } return task->get_future(); } #endif ================================================ FILE: QBase/Timer.cc ================================================ #include #include #include #include #include "Timer.h" uint64_t Now() { struct timeval now; ::gettimeofday(&now, 0); return uint64_t(now.tv_sec * 1000UL + now.tv_usec / 1000UL); } static bool IsLeapYear(int year) { return (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)); } static int DaysOfMonth(int year, int month) { const int monthDay[13] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (2 == month && IsLeapYear(year)) return 29; return monthDay[month]; } Time::Time() : ms_(0), valid_(false) { tm_.tm_year = 0; this->Now(); } Time::Time(const Time& other) : ms_(other.ms_) { tm_.tm_year = 0; valid_ = false; } Time::Time(int hour, int min, int sec) { this->Now(); //如果hour小于当前的hour,则换天了,如果当前天数是本月最后一天,则换月;如果本月是12月,则换年 int day = GetDay(); int mon = GetMonth(); int year = GetYear(); bool tomorrow = false; if (hour < GetHour() || (hour == GetHour() && min < GetMinute()) || (hour == GetHour() && min == GetMinute() && sec < GetSecond())) tomorrow = true; if (tomorrow) { if (DaysOfMonth(year, mon) == day) { day = 1; if (12 == mon) { mon = 1; ++ year; } else ++ mon; } else { ++ day; } } // 构造tm struct tm stm; stm.tm_sec = sec; stm.tm_min = min; stm.tm_hour= hour; stm.tm_mday= day; stm.tm_mon = mon - 1; stm.tm_year = year - 1900; stm.tm_yday = 0; stm.tm_isdst = 0; time_t tt = mktime(&stm); ms_ = tt * 1000UL; valid_ = false; } void Time::_UpdateTm() const { if (valid_) return; valid_ = true; const time_t now(ms_ / 1000UL); ::localtime_r(&now, &tm_); } void Time::Now() { ms_ = ::Now(); valid_ = false; } // from 2015 to 2025 static const char* YEAR[] = { "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", nullptr, }; std::size_t Time::FormatTime(char* buf) const { static char NUMBER[60][2] = {""}; static bool bFirst = true; if (bFirst) { bFirst = false; for (size_t i = 0; i < sizeof NUMBER / sizeof NUMBER[0]; ++ i) { char tmp[3]; snprintf(tmp, 3, "%02d", static_cast(i)); memcpy(NUMBER[i], tmp, 2); } } _UpdateTm(); #if 1 memcpy(buf, YEAR[tm_.tm_year + 1900 - 2015], 4); buf[4] = '-'; memcpy(buf + 5, NUMBER[tm_.tm_mon + 1], 2); buf[7] = '-'; memcpy(buf + 8, NUMBER[tm_.tm_mday], 2); buf[10] = '['; memcpy(buf + 11, NUMBER[tm_.tm_hour], 2); buf[13] = ':'; memcpy(buf + 14, NUMBER[tm_.tm_min], 2); buf[16] = ':'; memcpy(buf + 17, NUMBER[tm_.tm_sec], 2); buf[19] = '.'; snprintf(buf + 20, 5, "%03d]", static_cast(ms_ % 1000)); #else snprintf(buf, 25, "%04d-%02d-%02d[%02d:%02d:%02d.%03d]", tm_.tm_year+1900, tm_.tm_mon+1, tm_.tm_mday, tm_.tm_hour, tm_.tm_min, tm_.tm_sec, static_cast(ms_ % 1000)); #endif return 24; } void Time::AddDelay(uint64_t delay) { ms_ += delay; valid_ = false; } Time& Time::operator= (const Time & other) { if (this != &other) { ms_ = other.ms_; valid_ = false; } return *this; } Timer::Timer(): next_(nullptr), prev_(nullptr) { } void Timer::Init(uint32_t interval, int32_t count) { interval_ = interval; count_ = count; triggerTime_.Now(); triggerTime_.AddDelay(interval); } bool Timer::OnTimer() { if (!func_) return false; if (count_ < 0 || -- count_ >= 0) { triggerTime_.AddDelay(interval_); func_(); return true; } return false; } TimerManager::TimerManager() : count_(0) { for (int i = 0; i < LIST1_SIZE; ++ i) { m_list1[i] = new Timer(); } for (int i = 0; i < LIST_SIZE; ++ i) { m_list2[i] = new Timer(); m_list3[i] = new Timer(); m_list4[i] = new Timer(); m_list5[i] = new Timer(); } } TimerManager::~TimerManager() { Timer* pTimer = nullptr; for (int i = 0; i < LIST1_SIZE; ++ i) { while ((pTimer = m_list1[i]->next_) ) { KillTimer(pTimer); delete pTimer; } delete m_list1[i]; } for (int i = 0; i < LIST_SIZE; ++ i) { while ((pTimer = m_list2[i]->next_) ) { KillTimer(pTimer); delete pTimer; } delete m_list2[i]; while ((pTimer = m_list3[i]->next_) ) { KillTimer(pTimer); delete pTimer; } delete m_list3[i]; while ((pTimer = m_list4[i]->next_) ) { KillTimer(pTimer); delete pTimer; } delete m_list4[i]; while ((pTimer = m_list5[i]->next_) ) { KillTimer(pTimer); delete pTimer; } delete m_list5[i]; } for (auto t : freepool_) delete t; } TimerManager& TimerManager::Instance() { static TimerManager mgr; return mgr; } Timer* TimerManager::CreateTimer() { Timer* timer = nullptr; if (freepool_.empty()) { timer = new Timer(); } else { timer = *(freepool_.begin()); freepool_.erase(freepool_.begin()); } return timer; } bool TimerManager::UpdateTimers(const Time& now) { if (count_ > 0 && lock_.try_lock()) { decltype(timers_) tmp; tmp.swap(timers_); count_ = 0; lock_.unlock(); for (auto timer : tmp) { AddTimer(timer); } } const bool hasUpdated(m_lastCheckTime <= now); while (m_lastCheckTime <= now) { int index = m_lastCheckTime & (LIST1_SIZE - 1); if (index == 0 && !_Cacsade(m_list2, _Index(0)) && !_Cacsade(m_list3, _Index(1)) && !_Cacsade(m_list4, _Index(2))) { _Cacsade(m_list5, _Index(3)); } m_lastCheckTime.AddDelay(1); Timer* timer; while ((timer = m_list1[index]->next_)) { KillTimer(timer); if (timer->OnTimer()) AddTimer(timer); else freepool_.insert(timer); } } return hasUpdated; } void TimerManager::AddTimer(Timer* timer) { uint32_t diff = static_cast(timer->triggerTime_ - m_lastCheckTime); uint64_t trigTime = timer->triggerTime_.MilliSeconds(); Timer* pListHead = nullptr; if ((int32_t)diff < 0) { pListHead = m_list1[m_lastCheckTime.MilliSeconds() & (LIST1_SIZE - 1)]; } else if (diff < static_cast(LIST1_SIZE)) { pListHead = m_list1[trigTime & (LIST1_SIZE - 1)]; } else if (diff < 1 << (LIST1_BITS + LIST_BITS)) { pListHead = m_list2[(trigTime >> LIST1_BITS) & (LIST_SIZE - 1)]; } else if (diff < 1 << (LIST1_BITS + 2 * LIST_BITS)) { pListHead = m_list3[(trigTime >> (LIST1_BITS + LIST_BITS)) & (LIST_SIZE - 1)]; } else if (diff < 1 << (LIST1_BITS + 3 * LIST_BITS)) { pListHead = m_list4[(trigTime >> (LIST1_BITS + 2 * LIST_BITS)) & (LIST_SIZE - 1)]; } else { pListHead = m_list5[(trigTime >> (LIST1_BITS + 3 * LIST_BITS)) & (LIST_SIZE - 1)]; } assert(!pListHead->prev_); timer->prev_ = pListHead; timer->next_ = pListHead->next_; if (pListHead->next_) pListHead->next_->prev_ = timer; pListHead->next_ = timer; freepool_.erase(timer); } void TimerManager::AsyncAddTimer(Timer* timer) { std::lock_guard guard(lock_); timers_.push_back(timer); ++ count_; assert (count_ == timers_.size()); } void TimerManager::ScheduleAt(Timer* timer, const Time& triggerTime) { if (!timer) return; timer->triggerTime_ = triggerTime; AddTimer(timer); } void TimerManager::KillTimer(Timer* timer) { if (!timer) return; if (timer->prev_) { timer->prev_->next_ = timer->next_; if (timer->next_) { timer->next_->prev_ = timer->prev_; timer->next_ = nullptr; } timer->prev_ = nullptr; } } bool TimerManager::_Cacsade(Timer* pList[], int index) { assert (pList); if (index < 0 || index >= LIST_SIZE) return false; assert (pList[index]); if (!pList[index]->next_) return false; Timer* tmpListHead = pList[index]->next_; pList[index]->next_ = nullptr; while (tmpListHead) { Timer* next = tmpListHead->next_; tmpListHead->next_ = nullptr; tmpListHead->prev_ = nullptr; AddTimer(tmpListHead); tmpListHead = next; } return true; } ================================================ FILE: QBase/Timer.h ================================================ #ifndef BERT_TIMER_H #define BERT_TIMER_H #include #include #include #include #include #include #include #include #include uint64_t Now(); class Time { public: Time(); Time(const Time& other); Time(int hour, int min, int sec); // TODO year month day, and Effective cpp item 18 void Now(); uint64_t MilliSeconds() const { return ms_; } std::size_t FormatTime(char* buf) const; void AddDelay(uint64_t delay); int GetYear() const { _UpdateTm(); return tm_.tm_year + 1900; } int GetMonth() const { _UpdateTm(); return tm_.tm_mon + 1; } int GetDay() const { _UpdateTm(); return tm_.tm_mday; } int GetHour() const { _UpdateTm(); return tm_.tm_hour; } int GetMinute() const { _UpdateTm(); return tm_.tm_min; } int GetSecond() const { _UpdateTm(); return tm_.tm_sec; } Time& operator= (const Time& ); operator uint64_t() const { return ms_; } private: uint64_t ms_; // milliseconds from 1970 mutable tm tm_; mutable bool valid_; void _UpdateTm() const; }; class Timer { friend class TimerManager; public: void Init(uint32_t interval, int32_t count = -1); bool OnTimer(); void SetRemainCnt(int32_t remain) { count_ = remain; } bool IsWorking() const { return prev_ != nullptr; } template void SetCallback(F&& f, Args&&... args) { auto temp = std::bind(std::forward(f), std::forward(args)...); func_ = [temp]() { (void)temp(); }; } private: Timer(); std::function func_; Timer* next_; Timer* prev_; Time triggerTime_; uint32_t interval_; int32_t count_; }; class TimerManager { public: ~TimerManager(); TimerManager(const TimerManager& ) = delete; void operator= (const TimerManager& ) = delete; static TimerManager& Instance(); bool UpdateTimers(const Time& now); void ScheduleAt(Timer* pTimer, const Time& triggerTime); void AddTimer(Timer* timer); void AsyncAddTimer(Timer* timer); void KillTimer(Timer* pTimer); Timer* CreateTimer(); private: TimerManager(); bool _Cacsade(Timer* pList[], int index); int _Index(int level); static const int LIST1_BITS = 8; static const int LIST_BITS = 6; static const int LIST1_SIZE = 1 << LIST1_BITS; static const int LIST_SIZE = 1 << LIST_BITS; Time m_lastCheckTime; Timer* m_list1[LIST1_SIZE]; // 256 ms Timer* m_list2[LIST_SIZE]; // 64 * 256ms = 16s Timer* m_list3[LIST_SIZE]; // 64 * 64 * 256ms = 17m Timer* m_list4[LIST_SIZE]; // 64 * 64 * 64 * 256ms = 18h Timer* m_list5[LIST_SIZE]; // 64 * 64 * 64 * 64 * 256ms = 49 days // timer pool std::set freepool_; // async add std::mutex lock_; std::atomic count_; std::vector timers_; }; inline int TimerManager::_Index(int level) { uint64_t current = m_lastCheckTime; current >>= (LIST1_BITS + level * LIST_BITS); return current & (LIST_SIZE - 1); } #endif ================================================ FILE: QBase/UnboundedBuffer.cc ================================================ #include "UnboundedBuffer.h" #include #include #include namespace qedis { const std::size_t UnboundedBuffer::MAX_BUFFER_SIZE = std::numeric_limits::max() / 2; std::size_t UnboundedBuffer::Write(const void* pData, std::size_t nSize) { return PushData(pData, nSize); } std::size_t UnboundedBuffer::PushData(const void* pData, std::size_t nSize) { std::size_t nBytes = PushDataAt(pData, nSize); AdjustWritePtr(nBytes); return nBytes; } std::size_t UnboundedBuffer::PushDataAt(const void* pData, std::size_t nSize, std::size_t offset) { if (!pData || nSize == 0) return 0; if (ReadableSize() == UnboundedBuffer::MAX_BUFFER_SIZE) return 0; _AssureSpace(nSize + offset); assert (nSize + offset <= WritableSize()); ::memcpy(&buffer_[writePos_ + offset], pData, nSize); return nSize; } std::size_t UnboundedBuffer::PeekData(void* pBuf, std::size_t nSize) { std::size_t nBytes = PeekDataAt(pBuf, nSize); AdjustReadPtr(nBytes); return nBytes; } std::size_t UnboundedBuffer::PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset) { const std::size_t dataSize = ReadableSize(); if (!pBuf || nSize == 0 || dataSize <= offset) return 0; if (nSize + offset > dataSize) nSize = dataSize - offset; ::memcpy(pBuf, &buffer_[readPos_ + offset], nSize); return nSize; } void UnboundedBuffer::_AssureSpace(std::size_t nSize) { if (nSize <= WritableSize()) return; std::size_t maxSize = buffer_.size(); while (nSize > WritableSize() + readPos_) { if (maxSize < 64) maxSize = 64; else if (maxSize <= UnboundedBuffer::MAX_BUFFER_SIZE) maxSize += (maxSize / 2); else break; buffer_.resize(maxSize); } if (readPos_ > 0) { std::size_t dataSize = ReadableSize(); std::cout << dataSize << " bytes moved from " << readPos_ << std::endl; ::memmove(&buffer_[0], &buffer_[readPos_], dataSize); readPos_ = 0; writePos_ = dataSize; } } void UnboundedBuffer::Shrink(bool tight) { assert (buffer_.capacity() == buffer_.size()); if (buffer_.empty()) { assert (readPos_ == 0); assert (writePos_ == 0); return; } std::size_t oldCap = buffer_.size(); std::size_t dataSize = ReadableSize(); if (!tight && dataSize > oldCap / 2) return; std::vector tmp; tmp.resize(dataSize); memcpy(&tmp[0], &buffer_[readPos_], dataSize); tmp.swap(buffer_); readPos_ = 0; writePos_ = dataSize; std::cout << oldCap << " shrink to " << buffer_.size() << std::endl; } void UnboundedBuffer::Clear() { readPos_ = writePos_ = 0; } void UnboundedBuffer::Swap(UnboundedBuffer& buf) { buffer_.swap(buf.buffer_); std::swap(readPos_, buf.readPos_); std::swap(writePos_, buf.writePos_); } #if 0 int main() { UnboundedBuffer buf; std::size_t ret = buf.PushData("hello", 5); assert (ret == 5); char tmp[10]; ret = buf.PeekData(tmp, sizeof tmp); assert(ret == 5); assert(tmp[0] == 'h'); assert(buf.IsEmpty()); ret = buf.PushData("world", 5); assert (ret == 5); ret = buf.PushData("abcde", 5); assert (ret == 5); ret = buf.PeekData(tmp, 5); assert(tmp[0] == 'w'); buf.Clear(); buf.Shrink(); #if 1 ret = buf.PeekData(tmp, 5); if (ret == 5) { assert(tmp[0] == 'a'); assert(tmp[1] == 'b'); } #endif buf.Shrink(); return 0; } #endif } ================================================ FILE: QBase/UnboundedBuffer.h ================================================ #ifndef BERT_UNBOUNDEDBUFFER_H #define BERT_UNBOUNDEDBUFFER_H #include #include namespace qedis { class UnboundedBuffer { public: UnboundedBuffer() : readPos_(0), writePos_(0) { } std::size_t PushDataAt(const void* pData, std::size_t nSize, std::size_t offset = 0); std::size_t PushData(const void* pData, std::size_t nSize); std::size_t Write(const void* pData, std::size_t nSize); void AdjustWritePtr(std::size_t nBytes) { writePos_ += nBytes; } std::size_t PeekDataAt(void* pBuf, std::size_t nSize, std::size_t offset = 0); std::size_t PeekData(void* pBuf, std::size_t nSize); void AdjustReadPtr(std::size_t nBytes) { readPos_ += nBytes; } char* ReadAddr() { return &buffer_[readPos_]; } char* WriteAddr() { return &buffer_[writePos_]; } bool IsEmpty() const { return ReadableSize() == 0; } std::size_t ReadableSize() const { return writePos_ - readPos_; } std::size_t WritableSize() const { return buffer_.size() - writePos_; } void Shrink(bool tight = false); void Clear(); void Swap(UnboundedBuffer& buf); static const std::size_t MAX_BUFFER_SIZE; private: void _AssureSpace(std::size_t size); std::size_t readPos_; std::size_t writePos_; std::vector buffer_; }; } #endif ================================================ FILE: QBase/lzf/lzf.h ================================================ /* * Copyright (c) 2000-2008 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #ifndef LZF_H #define LZF_H /*********************************************************************** ** ** lzf -- an extremely fast/free compression/decompression-method ** http://liblzf.plan9.de/ ** ** This algorithm is believed to be patent-free. ** ***********************************************************************/ #define LZF_VERSION 0x0105 /* 1.5, API version */ /* * Compress in_len bytes stored at the memory block starting at * in_data and write the result to out_data, up to a maximum length * of out_len bytes. * * If the output buffer is not large enough or any error occurs return 0, * otherwise return the number of bytes used, which might be considerably * more than in_len (but less than 104% of the original size), so it * makes sense to always use out_len == in_len - 1), to ensure _some_ * compression, and store the data uncompressed otherwise (with a flag, of * course. * * lzf_compress might use different algorithms on different systems and * even different runs, thus might result in different compressed strings * depending on the phase of the moon or similar factors. However, all * these strings are architecture-independent and will result in the * original data when decompressed using lzf_decompress. * * The buffers must not be overlapping. * * If the option LZF_STATE_ARG is enabled, an extra argument must be * supplied which is not reflected in this header file. Refer to lzfP.h * and lzf_c.c. * */ unsigned int lzf_compress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len); /* * Decompress data compressed with some version of the lzf_compress * function and stored at location in_data and length in_len. The result * will be stored at out_data up to a maximum of out_len characters. * * If the output buffer is not large enough to hold the decompressed * data, a 0 is returned and errno is set to E2BIG. Otherwise the number * of decompressed bytes (i.e. the original length of the data) is * returned. * * If an error in the compressed data is detected, a zero is returned and * errno is set to EINVAL. * * This function is very fast, about as fast as a copying loop. */ unsigned int lzf_decompress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len); #endif ================================================ FILE: QBase/lzf/lzfP.h ================================================ /* * Copyright (c) 2000-2007 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #ifndef LZFP_h #define LZFP_h #define STANDALONE 1 /* at the moment, this is ok. */ #ifndef STANDALONE # include "lzf.h" #endif /* * Size of hashtable is (1 << HLOG) * sizeof (char *) * decompression is independent of the hash table size * the difference between 15 and 14 is very small * for small blocks (and 14 is usually a bit faster). * For a low-memory/faster configuration, use HLOG == 13; * For best compression, use 15 or 16 (or more, up to 23). */ #ifndef HLOG # define HLOG 16 #endif /* * Sacrifice very little compression quality in favour of compression speed. * This gives almost the same compression as the default code, and is * (very roughly) 15% faster. This is the preferred mode of operation. */ #ifndef VERY_FAST # define VERY_FAST 1 #endif /* * Sacrifice some more compression quality in favour of compression speed. * (roughly 1-2% worse compression for large blocks and * 9-10% for small, redundant, blocks and >>20% better speed in both cases) * In short: when in need for speed, enable this for binary data, * possibly disable this for text data. */ #ifndef ULTRA_FAST # define ULTRA_FAST 0 #endif /* * Unconditionally aligning does not cost very much, so do it if unsure */ #ifndef STRICT_ALIGN # define STRICT_ALIGN !(defined(__i386) || defined (__amd64)) #endif /* * You may choose to pre-set the hash table (might be faster on some * modern cpus and large (>>64k) blocks, and also makes compression * deterministic/repeatable when the configuration otherwise is the same). */ #ifndef INIT_HTAB # define INIT_HTAB 0 #endif /* * Avoid assigning values to errno variable? for some embedding purposes * (linux kernel for example), this is necessary. NOTE: this breaks * the documentation in lzf.h. */ #ifndef AVOID_ERRNO # define AVOID_ERRNO 0 #endif /* * Whether to pass the LZF_STATE variable as argument, or allocate it * on the stack. For small-stack environments, define this to 1. * NOTE: this breaks the prototype in lzf.h. */ #ifndef LZF_STATE_ARG # define LZF_STATE_ARG 0 #endif /* * Whether to add extra checks for input validity in lzf_decompress * and return EINVAL if the input stream has been corrupted. This * only shields against overflowing the input buffer and will not * detect most corrupted streams. * This check is not normally noticeable on modern hardware * (<1% slowdown), but might slow down older cpus considerably. */ #ifndef CHECK_INPUT # define CHECK_INPUT 1 #endif /*****************************************************************************/ /* nothing should be changed below */ typedef unsigned char u8; typedef const u8 *LZF_STATE[1 << (HLOG)]; #if !STRICT_ALIGN /* for unaligned accesses we need a 16 bit datatype. */ # include # if USHRT_MAX == 65535 typedef unsigned short u16; # elif UINT_MAX == 65535 typedef unsigned int u16; # else # undef STRICT_ALIGN # define STRICT_ALIGN 1 # endif #endif #if ULTRA_FAST # if defined(VERY_FAST) # undef VERY_FAST # endif #endif #if INIT_HTAB # ifdef __cplusplus # include # else # include # endif #endif #endif ================================================ FILE: QBase/lzf/lzf_c.c ================================================ /* * Copyright (c) 2000-2008 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "lzfP.h" #define HSIZE (1 << (HLOG)) /* * don't play with this unless you benchmark! * decompression is not dependent on the hash function * the hashing function might seem strange, just believe me * it works ;) */ #ifndef FRST # define FRST(p) (((p[0]) << 8) | p[1]) # define NEXT(v,p) (((v) << 8) | p[2]) # if ULTRA_FAST # define IDX(h) ((( h >> (3*8 - HLOG)) - h ) & (HSIZE - 1)) # elif VERY_FAST # define IDX(h) ((( h >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) # else # define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) # endif #endif /* * IDX works because it is very similar to a multiplicative hash, e.g. * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1)) * the latter is also quite fast on newer CPUs, and compresses similarly. * * the next one is also quite good, albeit slow ;) * (int)(cos(h & 0xffffff) * 1e6) */ #if 0 /* original lzv-like hash function, much worse and thus slower */ # define FRST(p) (p[0] << 5) ^ p[1] # define NEXT(v,p) ((v) << 5) ^ p[2] # define IDX(h) ((h) & (HSIZE - 1)) #endif #define MAX_LIT (1 << 5) #define MAX_OFF (1 << 13) #define MAX_REF ((1 << 8) + (1 << 3)) #if __GNUC__ >= 3 # define expect(expr,value) __builtin_expect ((expr),(value)) # define inline inline #else # define expect(expr,value) (expr) # define inline static #endif #define expect_false(expr) expect ((expr) != 0, 0) #define expect_true(expr) expect ((expr) != 0, 1) /* * compressed format * * 000LLLLL ; literal * LLLooooo oooooooo ; backref L * 111ooooo LLLLLLLL oooooooo ; backref L+7 * */ unsigned int lzf_compress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len #if LZF_STATE_ARG , LZF_STATE htab #endif ) { #if !LZF_STATE_ARG LZF_STATE htab; #endif const u8 **hslot; const u8 *ip = (const u8 *)in_data; u8 *op = (u8 *)out_data; const u8 *in_end = ip + in_len; u8 *out_end = op + out_len; const u8 *ref; /* off requires a type wide enough to hold a general pointer difference. * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only * works for differences within a single object). We also assume that no * no bit pattern traps. Since the only platform that is both non-POSIX * and fails to support both assumptions is windows 64 bit, we make a * special workaround for it. */ #if defined (WIN32) && defined (_M_X64) unsigned _int64 off; /* workaround for missing POSIX compliance */ #else unsigned long off; #endif unsigned int hval; int lit; if (!in_len || !out_len) return 0; #if INIT_HTAB memset (htab, 0, sizeof (htab)); # if 0 for (hslot = htab; hslot < htab + HSIZE; hslot++) *hslot++ = ip; # endif #endif lit = 0; op++; /* start run */ hval = FRST (ip); while (ip < in_end - 2) { hval = NEXT (hval, ip); hslot = htab + IDX (hval); ref = *hslot; *hslot = ip; if (1 #if INIT_HTAB && ref < ip /* the next test will actually take care of this, but this is faster */ #endif && (off = ip - ref - 1) < MAX_OFF && ip + 4 < in_end && ref > (u8 *)in_data #if STRICT_ALIGN && ref[0] == ip[0] && ref[1] == ip[1] && ref[2] == ip[2] #else && *(u16 *)ref == *(u16 *)ip && ref[2] == ip[2] #endif ) { /* match found at *ref++ */ unsigned int len = 2; unsigned int maxlen = (unsigned int)(in_end - ip - len); maxlen = maxlen > MAX_REF ? MAX_REF : maxlen; op [- lit - 1] = lit - 1; /* stop run */ op -= !lit; /* undo run if length is zero */ if (expect_false (op + 3 + 1 >= out_end)) return 0; for (;;) { if (expect_true (maxlen > 16)) { len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; len++; if (ref [len] != ip [len]) break; } do len++; while (len < maxlen && ref[len] == ip[len]); break; } len -= 2; /* len is now #octets - 1 */ ip++; if (len < 7) { *op++ = (off >> 8) + (len << 5); } else { *op++ = (off >> 8) + ( 7 << 5); *op++ = len - 7; } *op++ = off; lit = 0; op++; /* start run */ ip += len + 1; if (expect_false (ip >= in_end - 2)) break; #if ULTRA_FAST || VERY_FAST --ip; # if VERY_FAST && !ULTRA_FAST --ip; # endif hval = FRST (ip); hval = NEXT (hval, ip); htab[IDX (hval)] = ip; ip++; # if VERY_FAST && !ULTRA_FAST hval = NEXT (hval, ip); htab[IDX (hval)] = ip; ip++; # endif #else ip -= len + 1; do { hval = NEXT (hval, ip); htab[IDX (hval)] = ip; ip++; } while (len--); #endif } else { /* one more literal byte we must copy */ if (expect_false (op >= out_end)) return 0; lit++; *op++ = *ip++; if (expect_false (lit == MAX_LIT)) { op [- lit - 1] = lit - 1; /* stop run */ lit = 0; op++; /* start run */ } } } if (op + 3 > out_end) /* at most 3 bytes can be missing here */ return 0; while (ip < in_end) { lit++; *op++ = *ip++; if (expect_false (lit == MAX_LIT)) { op [- lit - 1] = lit - 1; /* stop run */ lit = 0; op++; /* start run */ } } op [- lit - 1] = lit - 1; /* end run */ op -= !lit; /* undo run if length is zero */ return (unsigned int)(op - (u8 *)out_data); } ================================================ FILE: QBase/lzf/lzf_d.c ================================================ /* * Copyright (c) 2000-2007 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "lzfP.h" #if AVOID_ERRNO # define SET_ERRNO(n) #else # include # define SET_ERRNO(n) errno = (n) #endif /* #if (__i386 || __amd64) && __GNUC__ >= 3 # define lzf_movsb(dst, src, len) \ asm ("rep movsb" \ : "=D" (dst), "=S" (src), "=c" (len) \ : "0" (dst), "1" (src), "2" (len)); #endif */ unsigned int lzf_decompress (const void *const in_data, unsigned int in_len, void *out_data, unsigned int out_len) { u8 const *ip = (const u8 *)in_data; u8 *op = (u8 *)out_data; u8 const *const in_end = ip + in_len; u8 *const out_end = op + out_len; do { unsigned int ctrl = *ip++; if (ctrl < (1 << 5)) /* literal run */ { ctrl++; if (op + ctrl > out_end) { SET_ERRNO (E2BIG); return 0; } #if CHECK_INPUT if (ip + ctrl > in_end) { SET_ERRNO (EINVAL); return 0; } #endif #ifdef lzf_movsb lzf_movsb (op, ip, ctrl); #else do *op++ = *ip++; while (--ctrl); #endif } else /* back reference */ { unsigned int len = ctrl >> 5; u8 *ref = op - ((ctrl & 0x1f) << 8) - 1; #if CHECK_INPUT if (ip >= in_end) { SET_ERRNO (EINVAL); return 0; } #endif if (len == 7) { len += *ip++; #if CHECK_INPUT if (ip >= in_end) { SET_ERRNO (EINVAL); return 0; } #endif } ref -= *ip++; if (op + len + 2 > out_end) { SET_ERRNO (E2BIG); return 0; } if (ref < (u8 *)out_data) { SET_ERRNO (EINVAL); return 0; } #ifdef lzf_movsb len += 2; lzf_movsb (op, ref, len); #else *op++ = *ref++; *op++ = *ref++; do *op++ = *ref++; while (--len); #endif } } while (ip < in_end); return (unsigned int)(op - (u8 *)out_data); } ================================================ FILE: QSentinel/CMakeLists.txt ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) LINK_DIRECTORIES(../../leveldb) AUX_SOURCE_DIRECTORY(. CLUSTER_SRC) AUX_SOURCE_DIRECTORY(./zookeeper CLUSTER_SRC) SET(LIBRARY_OUTPUT_PATH ../../bin) ADD_LIBRARY(qcluster SHARED ${CLUSTER_SRC}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore) TARGET_LINK_LIBRARIES(qcluster; qbaselib; qediscore; leveldb; pthread) ADD_DEPENDENCIES(qcluster qediscore qbaselib leveldb) SET_TARGET_PROPERTIES(qcluster PROPERTIES LINKER_LANGUAGE CXX) ================================================ FILE: QSentinel/QClusterClient.cc ================================================ #if QEDIS_CLUSTER #include "Log/Logger.h" #include "Server.h" #include "QConfig.h" #include "QCommand.h" #include "QClusterClient.h" #if USE_ZOOKEEPER #include "zookeeper/ZookeeperConn.h" #endif namespace qedis { PacketLength QClusterClient::_HandlePacket(const char* data, std::size_t bytes) { const char* ptr = data; bool result = conn_->ParseMessage(ptr, bytes); if (!result) { OnError(); return 0; } return static_cast(ptr - data); } bool QClusterClient::Init(int fd, const SocketAddr& peer) { if (!StreamSocket::Init(fd, peer)) return false; auto me = std::static_pointer_cast(shared_from_this()); SocketAddr myAddr(g_config.ip.c_str(), g_config.port); #if USE_ZOOKEEPER QClusterConn* conn = new ZookeeperConn(me, g_config.setid, myAddr.ToString()); #else #error "Only support zookeeper for now, supporting etcd is in progress" #endif conn->SetOnBecomeMaster([](const std::vector& slaves) { INF << "I become master"; std::vector cmd {"slaveof", "no", "one"}; slaveof(cmd, nullptr); for (const auto& addr : slaves) { INF << "Try connect to slave " << addr.ToString(); // connect to slaves and send 'slave of me' Server::Instance()->TCPConnect(addr, ConnectionTag::kSlaveClient); } // 读取所有set的数据保存其版本号 // *set的数据格式是 1,3,4,7|2:1,4 // 含义:本set负责slot 1347,但是现在正在将slot 1,4迁移到set2上 }); conn->SetOnBecomeSlave([](const std::string& master) { INF << "I become slave of " << master; std::vector cmd(SplitString(master, ':')); slaveof({"slaveof", cmd[0], cmd[1]}, nullptr); }); conn_.reset(conn); return true; } void QClusterClient::OnConnect() { conn_->OnConnect(); } void QClusterClient::OnDisconnect() { conn_->OnDisconnect(); } } // end namespace qedis #endif ================================================ FILE: QSentinel/QClusterClient.h ================================================ #ifndef BERT_QCLUSTERCLIENT_H #define BERT_QCLUSTERCLIENT_H #if QEDIS_CLUSTER #include "StreamSocket.h" #include "QClusterInterface.h" namespace ConnectionTag { const int kSentinelClient = 2; } namespace qedis { class QClusterClient: public StreamSocket { public: void OnConnect() override; void OnDisconnect() override; bool Init(int fd, const SocketAddr& peer); private: PacketLength _HandlePacket(const char*, std::size_t) override; std::unique_ptr conn_; }; } #endif // endif QEDIS_CLUSTER #endif // endif BERT_QCLUSTERCLIENT_H ================================================ FILE: QSentinel/QClusterInterface.h ================================================ #ifndef BERT_CLUSTERINTERFACE_H #define BERT_CLUSTERINTERFACE_H #if QEDIS_CLUSTER #include #include #include #include "Socket.h" namespace ConnectionTag { const int kSlaveClient = 3; } namespace qedis { class QClusterConn { public: virtual ~QClusterConn() { } void SetOnBecomeMaster(std::function& )> cb) { onBecomeMaster_ = std::move(cb); } void SetOnBecomeSlave(std::function cb) { onBecomeSlave_ = std::move(cb); } public: virtual bool ParseMessage(const char*& data, size_t len) = 0; virtual void OnConnect() = 0; virtual void OnDisconnect() = 0; protected: std::function& )> onBecomeMaster_; std::function onBecomeSlave_; }; } // end namespace qedis #endif // endif QEDIS_CLUSTER #endif // endif BERT_CLUSTERINTERFACE_H ================================================ FILE: QSentinel/README.md ================================================ # Qedis高可用集群 利用Zookeeper实现的Qedis高可用集群,功能类似redis-sentinel。在此基础上可实现sacle-out集群。 ## 环境需求 * C++11、CMake * zookeeper * Linux 或 MAC OS ## 集群特性 搭建Zookeeper,监视一组互为主备的Qedis进程以实现高可用; 当然也可以使用官方redis-sentinel。 scale-out集群正在开发中... ## 简单原理 /servers /set-1 /qedis-(127.0.0.1:6379)-0001 /qedis-(127.0.0.1:6381)-0003 /qedis-(127.0.0.1:6382)-0004 /qedis-(127.0.0.1:6385)-0007 /set-2 /qedis-(127.0.0.1:16379)-0004 /qedis-(127.0.0.1:16389)-0007 /qedis-(127.0.0.1:16399)-0008 一组Qedis进程形成一个set,set内最多只有一个master,其它都是slave,且没有级联复制结构。 通过配置文件中setid来配置set,相同setid的Qedis进程将形成一个set。 通过设置配置文件中cluster开关,Qedis在启动时,将尝试向Zookeeper的/servers/set-{id}/下创建自己的临时顺序节点。 创建成功后,获取孩子列表,看自己的节点序号是不是最小。 如果是最小,则是master,向所有孩子发送slaveof my_addr的命令; 如果不是,则监视比自己序号大的孩子中序号最小的节点。比如我的序号是7,孩子列表序号是1,3,4,7,则我监视4节点。 当我收到监视的节点被删除的通知,则判断自己是否是master(因为启动时已经获得孩子列表了)。 是或不是,都继续重复上述过的逻辑。 ================================================ FILE: QSentinel/zookeeper/ZookeeperConn.cc ================================================ #if QEDIS_CLUSTER #include #include "ZookeeperConn.h" #include "Log/Logger.h" #include "QCommon.h" #include "zookeeper.jute.h" #include "proto.h" static int deserialize_prime_response(struct prime_struct* req, AttachedBuffer& buffer) { buffer >> req->len; req->len = ntohl(req->len); buffer >> req->protocolVersion; req->protocolVersion = ntohl(req->protocolVersion); buffer >> req->timeOut; req->timeOut = ntohl(req->timeOut); buffer >> req->sessionId; req->sessionId = htonll(req->sessionId); buffer >> req->passwd_len; req->passwd_len = ntohl(req->passwd_len); memcpy(req->passwd, buffer.ReadAddr(), sizeof(req->passwd)); return 0; } const int kTimeout = 15 * 1000; // ms namespace qedis { ZookeeperConn::ZookeeperConn(const std::shared_ptr& c, int setId, const std::string& addr) : xid_(0), setId_(setId), addr_(addr), state_(State::kNone), pingTimer_(nullptr), sock_(c) { lastPing_.tv_sec = 0; lastPing_.tv_usec = 0; sessionInfo_.passwd[0] = '\0'; sessionFile_ = "zk.session" + addr; } ZookeeperConn::~ZookeeperConn() { } bool ZookeeperConn::ParseMessage(const char*& data, size_t len) { switch (state_) { case State::kHandshaking: { if (len < HANDSHAKE_RSP_SIZE) return true; struct prime_struct rsp; AttachedBuffer buffer(const_cast(data), len); deserialize_prime_response(&rsp, buffer); data += sizeof(int) + rsp.len; if (!_ProcessHandshake(rsp)) return false; } break; case State::kConnected: { if (len < 4) return true; int thisLen = *(int*)data; thisLen = ntohl(thisLen); if (sizeof(thisLen) + thisLen > len) return true; struct ReplyHeader hdr; struct iarchive *ia = create_buffer_iarchive(const_cast(data) + 4, thisLen); deserialize_ReplyHeader(ia, "hdr", &hdr); // update zxid if (hdr.zxid > 0) sessionInfo_.lastSeenZxid = hdr.zxid; if (!_ProcessResponse(hdr, ia)) return false; data += thisLen + sizeof(thisLen); } break; default: assert (false); break; } return true; } void ZookeeperConn::OnConnect() { assert (state_ == State::kNone); { auto del = [this](FILE* fp) { ::fclose(fp); ::unlink(this->sessionFile_.c_str()); }; std::unique_ptr _(fopen(sessionFile_.data(), "rb"), del); FILE* const fp = _.get(); if (fp) fread(&sessionInfo_, sizeof sessionInfo_, 1, fp); } char buffer_req[HANDSHAKE_REQ_SIZE]; int len = sizeof(buffer_req); struct connect_req req; req.protocolVersion = 0; req.sessionId = sessionInfo_.sessionId; req.passwd_len = sizeof(req.passwd); req.timeOut = kTimeout; req.lastZxidSeen = sessionInfo_.lastSeenZxid; memcpy(req.passwd, sessionInfo_.passwd, req.passwd_len); StackBuffer buf; buf << htonl(len) << htonl(req.protocolVersion) << htonll(req.lastZxidSeen) << htonl(req.timeOut) << htonll(req.sessionId) << htonl(req.passwd_len); if (req.passwd_len > 0) buf.PushData(req.passwd, req.passwd_len); auto s = sock_.lock(); if (s) { state_ = State::kHandshaking; s->SendPacket(buf); } } void ZookeeperConn::OnDisconnect() { if (pingTimer_) TimerManager::Instance().KillTimer(pingTimer_); } static struct ACL _OPEN_ACL_UNSAFE_ACL[] = {{0x1f, {const_cast("world"), const_cast("anyone") } }}; struct ACL_vector ZOO_OPEN_ACL_UNSAFE = { 1, _OPEN_ACL_UNSAFE_ACL}; static std::string MakeParentNode(int setid) { std::string path("/servers/set-"); path += std::to_string(setid); return path; } // /servers/set-{setid}/qedis(ip:port)-xxxseq static std::string MakeNodePath(int setid, const std::string& addr) { std::string path(MakeParentNode(setid)); path += "/qedis(" + addr + ")-"; return path; } static int GetNodeSeq(const std::string& path) { // /servers/set-{setid}/qedis(ip:port)-xxxseq auto pos = path.find_last_of('-'); if (pos == std::string::npos) return -1; std::string number(path.substr(pos + 1)); return std::stoi(number); } static std::string GetNodeAddr(const std::string& path) { // /servers/set-{setid}/qedis(ip:port)-xxxseq auto start = path.find_first_of('('); auto end = path.find_first_of(')'); if (start == std::string::npos || end == std::string::npos) return std::string(); return path.substr(start + 1, end - start - 1); } void ZookeeperConn::_RunForMaster(int setid, const std::string& value) { INF << __FUNCTION__ << ", setid " << setid << ", value " << value; struct oarchive* oa = create_buffer_oarchive(); struct RequestHeader h = { STRUCT_INITIALIZER (xid , _GetXid()), STRUCT_INITIALIZER (type ,ZOO_CREATE_OP) }; int rc = serialize_RequestHeader(oa, "header", &h); if (rc < 0) return; std::string path(MakeNodePath(setid, addr_)); struct CreateRequest req; req.path = const_cast(path.data()); req.data.buff = const_cast(value.data()); req.data.len = static_cast(value.size()); req.flags = ZOO_SEQUENCE | ZOO_EPHEMERAL; req.acl = ZOO_OPEN_ACL_UNSAFE; rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req); _SendPacket(h, oa); } int ZookeeperConn::_GetXid() const { return ++ xid_; } bool ZookeeperConn::_ProcessHandshake(const prime_struct& rsp) { if (sessionInfo_.sessionId && sessionInfo_.sessionId != rsp.sessionId) { DBG << "expired, new session " << rsp.sessionId; return false; } const bool resumedSession = (sessionInfo_.sessionId == rsp.sessionId); if (resumedSession) DBG << "resume session Id " << rsp.sessionId; else DBG << "new session Id " << rsp.sessionId; sessionInfo_.sessionId = rsp.sessionId; memcpy(sessionInfo_.passwd, rsp.passwd, rsp.passwd_len); std::unique_ptr _(fopen(sessionFile_.data(), "wb"), fclose); FILE* fp = _.get(); fwrite(&sessionInfo_, sizeof sessionInfo_, 1, fp); state_ = State::kConnected; _InitPingTimer(); if (resumedSession) { // My node must exists if (!_GetSiblings(MakeParentNode(setId_))) return false; } else { // Create my node _RunForMaster(setId_, addr_); } return true; } void ZookeeperConn::_InitPingTimer() { assert (!pingTimer_); pingTimer_ = TimerManager::Instance().CreateTimer(); pingTimer_->Init(kTimeout / 2); pingTimer_->SetCallback([this]() { struct oarchive *oa = create_buffer_oarchive(); struct RequestHeader h = { STRUCT_INITIALIZER(xid, PING_XID), STRUCT_INITIALIZER (type , ZOO_PING_OP) }; serialize_RequestHeader(oa, "header", &h); gettimeofday(&this->lastPing_, nullptr); _SendPacket(h, oa); }); TimerManager::Instance().AsyncAddTimer(pingTimer_); } bool ZookeeperConn::_IsMaster() const { if (siblings_.empty()) return false; auto me = siblings_.find(seq_); assert (me != siblings_.end()); return me == siblings_.begin(); } bool ZookeeperConn::_ProcessWatchEvent(const ReplyHeader& hdr, iarchive* ia) { struct WatcherEvent evt; deserialize_WatcherEvent(ia, "event", &evt); INF << "WatcherEventType " << evt.type << ", state " << evt.state << ", path " << evt.path; switch (evt.type) { case DELETED_EVENT_DEF: _OnNodeDelete(evt.path); break; default: break; } deallocate_WatcherEvent(&evt); return true; } bool ZookeeperConn::_ProcessResponse(const ReplyHeader& hdr, iarchive* ia) { if (hdr.xid == WATCHER_EVENT_XID) { return _ProcessWatchEvent(hdr, ia); } // TODO process some other watcher events if (pendingRequests_.empty()) { ERR << "Can not find request " << hdr.xid; return false; } const Request& req = pendingRequests_.front(); QEDIS_DEFER { pendingRequests_.pop_front(); }; if (req.xid != hdr.xid) { ERR << "wrong req xid " << req.xid << ", wrong order response " << hdr.xid; return false; } if (req.type != ZOO_PING_OP) INF << "req.type " << req.type; switch (req.type) { case ZOO_PING_OP: { timeval now; gettimeofday(&now, nullptr); int microseconds = (now.tv_sec - lastPing_.tv_sec) * 1000000; microseconds += (now.tv_usec - lastPing_.tv_usec); if (microseconds > 10 * 1000) WRN << "recv ping used microseconds " << microseconds; } break; case ZOO_CREATE_OP: { CreateResponse rsp; if (deserialize_CreateResponse(ia, "rsp", &rsp) != 0) { ERR << "deserialize_CreateResponse failed"; return false; } QEDIS_DEFER { deallocate_CreateResponse(&rsp); }; assert (node_.empty()); node_ = rsp.path; seq_ = GetNodeSeq(node_); assert (seq_ >= 0); DBG << "my node seq " << seq_ << " for my node " << node_ << ", addr " << GetNodeAddr(node_); if (!_GetSiblings(MakeParentNode(setId_))) return false; } break; case ZOO_GETCHILDREN2_OP: { GetChildren2Response rsp; if (deserialize_GetChildren2Response(ia, "rsp", &rsp) != 0) { ERR << "deserialize_GetChildren2Response failed"; return false; } QEDIS_DEFER { deallocate_GetChildren2Response(&rsp); }; siblings_.clear(); for (int i = 0; i < rsp.children.count; ++ i) { const std::string& node = rsp.children.data[i]; int seq = GetNodeSeq(node); assert (seq >= 0); if (node_.empty()) { std::string addr = GetNodeAddr(node); if (addr == addr_) { node_ = node; seq_ = seq; DBG << "Resumed session: my seq " << seq_ << " for my node " << node_ << ", addr " << GetNodeAddr(node_); } } INF << "Get sibling " << node; siblings_.insert({seq, node}); } auto me = siblings_.find(seq_); assert (me != siblings_.end()); if (me == siblings_.begin()) { // I am master!先获取节点分片信息,再执行slaveof no one等 if (!_GetData(MakeParentNode(setId_), true)) { ERR << "_GetData failed for set " << setId_; return false; } } else { // monitor the node bigger than me auto brother = -- me; // I'll watch you _Exists(MakeParentNode(setId_) + "/" + brother->second, true); } } break; case ZOO_GETDATA_OP: { GetDataResponse drsp; if (deserialize_GetDataResponse(ia, "rsp", &drsp) != 0) { ERR << "deserialize_GetDataResponse failed"; return false; } QEDIS_DEFER { deallocate_GetDataResponse(&drsp); }; // 获取了节点分片信息 std::vector slaves; slaves.reserve(siblings_.size()); auto me = siblings_.find(seq_); assert (me != siblings_.end()); auto slave = me; for (++ slave; slave != siblings_.end(); ++ slave) { SocketAddr addr(GetNodeAddr(slave->second)); slaves.push_back(addr); } #if 0 std::vector shardings; std::unordered_map > migration; // dst set & shardings { // *set的数据格式是 1,3,4,7|2:1,4 std::string data(drsp.data.buff, drsp.data.len); std::vector tmp(SplitString(data, '|')); std::vector shardingStr(SplitString(tmp[0], ',')); auto it = tmp.begin(); for (++ it; it != tmp.end(); ++ it) { std::vector dstAndShardingStr(SplitString(*it, ':')); assert (dstAndShardingStr.size() == 2); int dstSetId = std::stoi(dstAndShardingStr[0]); std::vector migrateStr(SplitString(dstAndShardingStr[1], ',')); std::vector migrates; migrates.reserve(migrateStr.size()); for (const auto& s : migrateStr) migrates.push_back(std::stoi(s)); migration[dstSetId] = migrates; } } #endif if (onBecomeMaster_) onBecomeMaster_(slaves); } break; case ZOO_EXISTS_OP: { if (hdr.err == ZNONODE) { _OnNodeDelete(req.path); } else { assert (hdr.err == ZOK); ExistsResponse rsp; if (deserialize_ExistsResponse(ia, "rsp", &rsp) != 0) { ERR << "deserialize_ExistsResponse failed"; return false; } QEDIS_DEFER { deallocate_ExistsResponse(&rsp); }; DBG << "Exists response version " << rsp.stat.version; if (onBecomeSlave_) { std::string master = GetNodeAddr(siblings_.begin()->second); if (master.empty()) return false; onBecomeSlave_(master); } } } break; default: break; } return true; } bool ZookeeperConn::_GetSiblings(const std::string& parent) { struct oarchive* oa = create_buffer_oarchive(); struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER (type ,ZOO_GETCHILDREN2_OP)}; struct GetChildren2Request req; req.path = const_cast(parent.data()); req.watch = 0; int rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, "req", &req); if (!_SendPacket(h, oa, &parent)) return false; return rc >= 0; } bool ZookeeperConn::_GetData(const std::string& node, bool watch) { struct oarchive* oa = create_buffer_oarchive(); struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER(type ,ZOO_GETDATA_OP)}; struct GetDataRequest req; req.path = const_cast(node.data()); req.watch = watch ? 1 : 0; int rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_GetDataRequest(oa, "req", &req); if (!_SendPacket(h, oa, &node)) return false; return rc >= 0; } bool ZookeeperConn::_Exists(const std::string& sibling, bool watch) { struct oarchive* oa = create_buffer_oarchive(); struct RequestHeader h = { STRUCT_INITIALIZER( xid, _GetXid()), STRUCT_INITIALIZER (type ,ZOO_EXISTS_OP)}; struct ExistsRequest req; req.path = const_cast(sibling.data()); req.watch = watch ? 1 : 0; int rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_ExistsRequest(oa, "req", &req); if (!_SendPacket(h, oa, &sibling)) return false; return rc >= 0; } bool ZookeeperConn::_SendPacket(const RequestHeader& h, struct oarchive* oa, const std::string* v) { auto s = sock_.lock(); if (!s) return false; int totalLen = htonl(get_buffer_len(oa)); s->SendPacket(&totalLen, sizeof totalLen); s->SendPacket(get_buffer(oa), get_buffer_len(oa)); Request r; r.xid = h.xid; r.type = h.type; if (v) r.path = *v; pendingRequests_.emplace_back(std::move(r)); close_buffer_oarchive(&oa, 1); return true; } void ZookeeperConn::_OnNodeDelete(const std::string& node) { const std::string siblingName = node.substr(node.find_last_of('/') + 1); const int seq = GetNodeSeq(siblingName); siblings_.erase(seq); if (_IsMaster()) { // Though I'll be master, I must broadcast this fact to all children. bool succ = _GetSiblings(MakeParentNode(setId_)); if (!succ) ERR << __FUNCTION__ << ", _GetSiblings failed with " << setId_; } else { auto me = siblings_.find(seq_); assert (me != siblings_.begin()); assert (me != siblings_.end()); auto brother = -- me; // I'll watch you _Exists(MakeParentNode(setId_) + "/" + brother->second, true); } } } // end namespace qedis #endif ================================================ FILE: QSentinel/zookeeper/ZookeeperConn.h ================================================ #ifndef BERT_ZOOKEEPERCONN_H #define BERT_ZOOKEEPERCONN_H #include #include #include "StreamSocket.h" #include "../QClusterInterface.h" struct prime_struct; struct RequestHeader; struct ReplyHeader; struct iarchive; struct oarchive; class Timer; namespace qedis { class ZookeeperConn : public QClusterConn { public: ZookeeperConn(const std::shared_ptr& c, int setId, const std::string& addr); ~ZookeeperConn(); bool ParseMessage(const char*& data, size_t len) override; void OnConnect() override; void OnDisconnect() override; private: void _RunForMaster(int setid, const std::string& val); bool _ProcessHandshake(const prime_struct& rsp); bool _ProcessResponse(const ReplyHeader& header, iarchive* ia); bool _ProcessWatchEvent(const ReplyHeader& header, iarchive* ia); bool _GetSiblings(const std::string& parent); bool _GetData(const std::string& node, bool watch = true); bool _Exists(const std::string& sibling, bool watch = true); void _InitPingTimer(); bool _IsMaster() const; bool _SendPacket(const RequestHeader& h, struct oarchive* oa, const std::string* = nullptr); void _OnNodeDelete(const std::string& node); int _GetXid() const; mutable int xid_; const int setId_; const std::string addr_; enum class State { kNone, kHandshaking, kConnected, } state_; struct Request { int type; int xid; std::string path; }; std::list pendingRequests_; timeval lastPing_; // my node & seq std::string node_; int seq_; // nodes in my set std::map siblings_; #pragma pack(1) struct SessionInfo { int64_t sessionId{0}; char passwd[16]; // ? zk_cli doesn't persist this field int64_t lastSeenZxid {0}; } sessionInfo_; #pragma pack() std::string sessionFile_; Timer* pingTimer_; std::weak_ptr sock_; }; } // end namespace qedis #endif //endif BERT_ZOOKEEPERCONN_H ================================================ FILE: QSentinel/zookeeper/proto.h ================================================ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PROTO_H_ #define PROTO_H_ #ifdef __cplusplus extern "C" { #endif const int ZOO_EPHEMERAL = 1 << 0; const int ZOO_SEQUENCE = 1 << 1; #define ZOO_NOTIFY_OP 0 #define ZOO_CREATE_OP 1 #define ZOO_DELETE_OP 2 #define ZOO_EXISTS_OP 3 #define ZOO_GETDATA_OP 4 #define ZOO_SETDATA_OP 5 #define ZOO_GETACL_OP 6 #define ZOO_SETACL_OP 7 #define ZOO_GETCHILDREN_OP 8 #define ZOO_SYNC_OP 9 #define ZOO_PING_OP 11 #define ZOO_GETCHILDREN2_OP 12 #define ZOO_CHECK_OP 13 #define ZOO_MULTI_OP 14 #define ZOO_CLOSE_OP -11 #define ZOO_SETAUTH_OP 100 #define ZOO_SETWATCHES_OP 101 /* the size of connect request */ #define HANDSHAKE_REQ_SIZE 44 /* connect request */ struct connect_req { int32_t protocolVersion; int64_t lastZxidSeen; int32_t timeOut; int64_t sessionId; int32_t passwd_len; char passwd[16]; }; #define HANDSHAKE_RSP_SIZE 40 /* the connect response */ struct prime_struct { int32_t len; int32_t protocolVersion; int32_t timeOut; int64_t sessionId; int32_t passwd_len; char passwd[16]; }; enum ZOO_ERRORS { ZOK = 0, /*!< Everything is OK */ /** System and server-side errors. * This is never thrown by the server, it shouldn't be used other than * to indicate a range. Specifically error codes greater than this * value, but lesser than {@link #ZAPIERROR}, are system errors. */ ZSYSTEMERROR = -1, ZRUNTIMEINCONSISTENCY = -2, /*!< A runtime inconsistency was found */ ZDATAINCONSISTENCY = -3, /*!< A data inconsistency was found */ ZCONNECTIONLOSS = -4, /*!< Connection to the server has been lost */ ZMARSHALLINGERROR = -5, /*!< Error while marshalling or unmarshalling data */ ZUNIMPLEMENTED = -6, /*!< Operation is unimplemented */ ZOPERATIONTIMEOUT = -7, /*!< Operation timeout */ ZBADARGUMENTS = -8, /*!< Invalid arguments */ ZINVALIDSTATE = -9, /*!< Invliad zhandle state */ /** API errors. * This is never thrown by the server, it shouldn't be used other than * to indicate a range. Specifically error codes greater than this * value are API errors (while values less than this indicate a * {@link #ZSYSTEMERROR}). */ ZAPIERROR = -100, ZNONODE = -101, /*!< Node does not exist */ ZNOAUTH = -102, /*!< Not authenticated */ ZBADVERSION = -103, /*!< Version conflict */ ZNOCHILDRENFOREPHEMERALS = -108, /*!< Ephemeral nodes may not have children */ ZNODEEXISTS = -110, /*!< The node already exists */ ZNOTEMPTY = -111, /*!< The node has children */ ZSESSIONEXPIRED = -112, /*!< The session has been expired by the server */ ZINVALIDCALLBACK = -113, /*!< Invalid callback specified */ ZINVALIDACL = -114, /*!< Invalid ACL specified */ ZAUTHFAILED = -115, /*!< Client authentication failed */ ZCLOSING = -116, /*!< ZooKeeper is closing */ ZNOTHING = -117, /*!< (not error) no server responses to process */ ZSESSIONMOVED = -118 /*! #include #include #include #include void deallocate_String(char **s) { if (*s) free(*s); *s = 0; } void deallocate_Buffer(struct buffer *b) { if (b->buff) free(b->buff); b->buff = 0; } struct buff_struct { int32_t len; int32_t off; char *buffer; }; static int resize_buffer(struct buff_struct *s, int newlen) { char *buffer= NULL; while (s->len < newlen) { s->len *= 2; } buffer = (char*)realloc(s->buffer, s->len); if (!buffer) { s->buffer = 0; return -ENOMEM; } s->buffer = buffer; return 0; } int oa_start_record(struct oarchive *oa, const char *tag) { return 0; } int oa_end_record(struct oarchive *oa, const char *tag) { return 0; } int oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d) { struct buff_struct *priv = oa->priv; int32_t i = htonl(*d); if ((priv->len - priv->off) < sizeof(i)) { int rc = resize_buffer(priv, priv->len + sizeof(i)); if (rc < 0) return rc; } memcpy(priv->buffer+priv->off, &i, sizeof(i)); priv->off+=sizeof(i); return 0; } #ifndef __APPLE__ int64_t htonll(int64_t v) { int i = 0; char *s = (char *)&v; if (htonl(1) == 1) { return v; } for (i = 0; i < 4; i++) { int tmp = s[i]; s[i] = s[8-i-1]; s[8-i-1] = tmp; } return v; } #endif int oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d) { const int64_t i = htonll(*d); struct buff_struct *priv = oa->priv; if ((priv->len - priv->off) < sizeof(i)) { int rc = resize_buffer(priv, priv->len + sizeof(i)); if (rc < 0) return rc; } memcpy(priv->buffer+priv->off, &i, sizeof(i)); priv->off+=sizeof(i); return 0; } int oa_start_vector(struct oarchive *oa, const char *tag, const int32_t *count) { return oa_serialize_int(oa, tag, count); } int oa_end_vector(struct oarchive *oa, const char *tag) { return 0; } int oa_serialize_bool(struct oarchive *oa, const char *name, const int32_t *i) { //return oa_serialize_int(oa, name, i); struct buff_struct *priv = oa->priv; if ((priv->len - priv->off) < 1) { int rc = resize_buffer(priv, priv->len + 1); if (rc < 0) return rc; } priv->buffer[priv->off] = (*i == 0 ? '\0' : '\1'); priv->off++; return 0; } static const int32_t negone = -1; int oa_serialize_buffer(struct oarchive *oa, const char *name, const struct buffer *b) { struct buff_struct *priv = oa->priv; int rc; if (!b) { return oa_serialize_int(oa, "len", &negone); } rc = oa_serialize_int(oa, "len", &b->len); if (rc < 0) return rc; // this means a buffer of NUll // with size of -1. This is // waht we use in java serialization for NULL if (b->len == -1) { return rc; } if ((priv->len - priv->off) < b->len) { rc = resize_buffer(priv, priv->len + b->len); if (rc < 0) return rc; } memcpy(priv->buffer+priv->off, b->buff, b->len); priv->off += b->len; return 0; } int oa_serialize_string(struct oarchive *oa, const char *name, char **s) { struct buff_struct *priv = oa->priv; int32_t len; int rc; if (!*s) { oa_serialize_int(oa, "len", &negone); return 0; } len = strlen(*s); rc = oa_serialize_int(oa, "len", &len); if (rc < 0) return rc; if ((priv->len - priv->off) < len) { rc = resize_buffer(priv, priv->len + len); if (rc < 0) return rc; } memcpy(priv->buffer+priv->off, *s, len); priv->off += len; return 0; } int ia_start_record(struct iarchive *ia, const char *tag) { return 0; } int ia_end_record(struct iarchive *ia, const char *tag) { return 0; } int ia_deserialize_int(struct iarchive *ia, const char *tag, int32_t *count) { struct buff_struct *priv = ia->priv; if ((priv->len - priv->off) < sizeof(*count)) { return -E2BIG; } memcpy(count, priv->buffer+priv->off, sizeof(*count)); priv->off+=sizeof(*count); *count = ntohl(*count); return 0; } int ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count) { struct buff_struct *priv = ia->priv; int64_t v = 0; if ((priv->len - priv->off) < sizeof(*count)) { return -E2BIG; } memcpy(count, priv->buffer+priv->off, sizeof(*count)); priv->off+=sizeof(*count); v = htonll(*count); // htonll and ntohll do the same *count = v; return 0; } int ia_start_vector(struct iarchive *ia, const char *tag, int32_t *count) { return ia_deserialize_int(ia, tag, count); } int ia_end_vector(struct iarchive *ia, const char *tag) { return 0; } int ia_deserialize_bool(struct iarchive *ia, const char *name, int32_t *v) { struct buff_struct *priv = ia->priv; //fprintf(stderr, "Deserializing bool %d\n", priv->off); //return ia_deserialize_int(ia, name, v); if ((priv->len - priv->off) < 1) { return -E2BIG; } *v = priv->buffer[priv->off]; priv->off+=1; //fprintf(stderr, "Deserializing bool end %d\n", priv->off); return 0; } int ia_deserialize_buffer(struct iarchive *ia, const char *name, struct buffer *b) { struct buff_struct *priv = ia->priv; int rc = ia_deserialize_int(ia, "len", &b->len); if (rc < 0) return rc; if ((priv->len - priv->off) < b->len) { return -E2BIG; } // set the buffer to null if (b->len == -1) { b->buff = NULL; return rc; } b->buff = malloc(b->len); if (!b->buff) { return -ENOMEM; } memcpy(b->buff, priv->buffer+priv->off, b->len); priv->off += b->len; return 0; } int ia_deserialize_string(struct iarchive *ia, const char *name, char **s) { struct buff_struct *priv = ia->priv; int32_t len; int rc = ia_deserialize_int(ia, "len", &len); if (rc < 0) return rc; if ((priv->len - priv->off) < len) { return -E2BIG; } if (len < 0) { return -EINVAL; } *s = malloc(len+1); if (!*s) { return -ENOMEM; } memcpy(*s, priv->buffer+priv->off, len); (*s)[len] = '\0'; priv->off += len; return 0; } static struct iarchive ia_default = { STRUCT_INITIALIZER (start_record ,ia_start_record), STRUCT_INITIALIZER (end_record ,ia_end_record), STRUCT_INITIALIZER (start_vector , ia_start_vector), STRUCT_INITIALIZER (end_vector ,ia_end_vector), STRUCT_INITIALIZER (deserialize_Bool , ia_deserialize_bool), STRUCT_INITIALIZER (deserialize_Int ,ia_deserialize_int), STRUCT_INITIALIZER (deserialize_Long , ia_deserialize_long) , STRUCT_INITIALIZER (deserialize_Buffer, ia_deserialize_buffer), STRUCT_INITIALIZER (deserialize_String, ia_deserialize_string) }; static struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_start_record), STRUCT_INITIALIZER (end_record , oa_end_record), STRUCT_INITIALIZER (start_vector , oa_start_vector), STRUCT_INITIALIZER (end_vector , oa_end_vector), STRUCT_INITIALIZER (serialize_Bool , oa_serialize_bool), STRUCT_INITIALIZER (serialize_Int , oa_serialize_int), STRUCT_INITIALIZER (serialize_Long , oa_serialize_long) , STRUCT_INITIALIZER (serialize_Buffer , oa_serialize_buffer), STRUCT_INITIALIZER (serialize_String , oa_serialize_string) }; struct iarchive *create_buffer_iarchive(char *buffer, int len) { struct iarchive *ia = malloc(sizeof(*ia)); struct buff_struct *buff = malloc(sizeof(struct buff_struct)); if (!ia) return 0; if (!buff) { free(ia); return 0; } *ia = ia_default; buff->off = 0; buff->buffer = buffer; buff->len = len; ia->priv = buff; return ia; } struct oarchive *create_buffer_oarchive() { struct oarchive *oa = malloc(sizeof(*oa)); struct buff_struct *buff = malloc(sizeof(struct buff_struct)); if (!oa) return 0; if (!buff) { free(oa); return 0; } *oa = oa_default; buff->off = 0; buff->buffer = malloc(128); buff->len = 128; oa->priv = buff; return oa; } void close_buffer_iarchive(struct iarchive **ia) { free((*ia)->priv); free(*ia); *ia = 0; } void close_buffer_oarchive(struct oarchive **oa, int free_buffer) { if (free_buffer) { struct buff_struct *buff = (struct buff_struct *)(*oa)->priv; if (buff->buffer) { free(buff->buffer); } } free((*oa)->priv); free(*oa); *oa = 0; } char *get_buffer(struct oarchive *oa) { struct buff_struct *buff = oa->priv; return buff->buffer; } int get_buffer_len(struct oarchive *oa) { struct buff_struct *buff = oa->priv; return buff->off; } ================================================ FILE: QSentinel/zookeeper/recordio.h ================================================ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __RECORDIO_H__ #define __RECORDIO_H__ #include #define STRUCT_INITIALIZER(l,r) .l = r #ifdef __cplusplus extern "C" { #endif struct buffer { int32_t len; char *buff; }; void deallocate_String(char **s); void deallocate_Buffer(struct buffer *b); void deallocate_vector(void *d); struct iarchive { int (*start_record)(struct iarchive *ia, const char *tag); int (*end_record)(struct iarchive *ia, const char *tag); int (*start_vector)(struct iarchive *ia, const char *tag, int32_t *count); int (*end_vector)(struct iarchive *ia, const char *tag); int (*deserialize_Bool)(struct iarchive *ia, const char *name, int32_t *); int (*deserialize_Int)(struct iarchive *ia, const char *name, int32_t *); int (*deserialize_Long)(struct iarchive *ia, const char *name, int64_t *); int (*deserialize_Buffer)(struct iarchive *ia, const char *name, struct buffer *); int (*deserialize_String)(struct iarchive *ia, const char *name, char **); void *priv; }; struct oarchive { int (*start_record)(struct oarchive *oa, const char *tag); int (*end_record)(struct oarchive *oa, const char *tag); int (*start_vector)(struct oarchive *oa, const char *tag, const int32_t *count); int (*end_vector)(struct oarchive *oa, const char *tag); int (*serialize_Bool)(struct oarchive *oa, const char *name, const int32_t *); int (*serialize_Int)(struct oarchive *oa, const char *name, const int32_t *); int (*serialize_Long)(struct oarchive *oa, const char *name, const int64_t *); int (*serialize_Buffer)(struct oarchive *oa, const char *name, const struct buffer *); int (*serialize_String)(struct oarchive *oa, const char *name, char **); void *priv; }; struct oarchive *create_buffer_oarchive(void); void close_buffer_oarchive(struct oarchive **oa, int free_buffer); struct iarchive *create_buffer_iarchive(char *buffer, int len); void close_buffer_iarchive(struct iarchive **ia); char *get_buffer(struct oarchive *); int get_buffer_len(struct oarchive *); #if defined(__APPLE__) #else int64_t htonll(int64_t v); #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: QSentinel/zookeeper/zookeeper.jute.c ================================================ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zookeeper.jute.h" int serialize_Id(struct oarchive *out, const char *tag, struct Id *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "scheme", &v->scheme); rc = rc ? rc : out->serialize_String(out, "id", &v->id); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_Id(struct iarchive *in, const char *tag, struct Id*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "scheme", &v->scheme); rc = rc ? rc : in->deserialize_String(in, "id", &v->id); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_Id(struct Id*v){ deallocate_String(&v->scheme); deallocate_String(&v->id); } int serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "perms", &v->perms); rc = rc ? rc : serialize_Id(out, "id", &v->id); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "perms", &v->perms); rc = rc ? rc : deserialize_Id(in, "id", &v->id); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ACL(struct ACL*v){ deallocate_Id(&v->id); } int serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "czxid", &v->czxid); rc = rc ? rc : out->serialize_Long(out, "mzxid", &v->mzxid); rc = rc ? rc : out->serialize_Long(out, "ctime", &v->ctime); rc = rc ? rc : out->serialize_Long(out, "mtime", &v->mtime); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->serialize_Int(out, "cversion", &v->cversion); rc = rc ? rc : out->serialize_Int(out, "aversion", &v->aversion); rc = rc ? rc : out->serialize_Long(out, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : out->serialize_Int(out, "dataLength", &v->dataLength); rc = rc ? rc : out->serialize_Int(out, "numChildren", &v->numChildren); rc = rc ? rc : out->serialize_Long(out, "pzxid", &v->pzxid); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "czxid", &v->czxid); rc = rc ? rc : in->deserialize_Long(in, "mzxid", &v->mzxid); rc = rc ? rc : in->deserialize_Long(in, "ctime", &v->ctime); rc = rc ? rc : in->deserialize_Long(in, "mtime", &v->mtime); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->deserialize_Int(in, "cversion", &v->cversion); rc = rc ? rc : in->deserialize_Int(in, "aversion", &v->aversion); rc = rc ? rc : in->deserialize_Long(in, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : in->deserialize_Int(in, "dataLength", &v->dataLength); rc = rc ? rc : in->deserialize_Int(in, "numChildren", &v->numChildren); rc = rc ? rc : in->deserialize_Long(in, "pzxid", &v->pzxid); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_Stat(struct Stat*v){ } int serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "czxid", &v->czxid); rc = rc ? rc : out->serialize_Long(out, "mzxid", &v->mzxid); rc = rc ? rc : out->serialize_Long(out, "ctime", &v->ctime); rc = rc ? rc : out->serialize_Long(out, "mtime", &v->mtime); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->serialize_Int(out, "cversion", &v->cversion); rc = rc ? rc : out->serialize_Int(out, "aversion", &v->aversion); rc = rc ? rc : out->serialize_Long(out, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : out->serialize_Long(out, "pzxid", &v->pzxid); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "czxid", &v->czxid); rc = rc ? rc : in->deserialize_Long(in, "mzxid", &v->mzxid); rc = rc ? rc : in->deserialize_Long(in, "ctime", &v->ctime); rc = rc ? rc : in->deserialize_Long(in, "mtime", &v->mtime); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->deserialize_Int(in, "cversion", &v->cversion); rc = rc ? rc : in->deserialize_Int(in, "aversion", &v->aversion); rc = rc ? rc : in->deserialize_Long(in, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : in->deserialize_Long(in, "pzxid", &v->pzxid); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_StatPersisted(struct StatPersisted*v){ } int serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "czxid", &v->czxid); rc = rc ? rc : out->serialize_Long(out, "mzxid", &v->mzxid); rc = rc ? rc : out->serialize_Long(out, "ctime", &v->ctime); rc = rc ? rc : out->serialize_Long(out, "mtime", &v->mtime); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->serialize_Int(out, "cversion", &v->cversion); rc = rc ? rc : out->serialize_Int(out, "aversion", &v->aversion); rc = rc ? rc : out->serialize_Long(out, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "czxid", &v->czxid); rc = rc ? rc : in->deserialize_Long(in, "mzxid", &v->mzxid); rc = rc ? rc : in->deserialize_Long(in, "ctime", &v->ctime); rc = rc ? rc : in->deserialize_Long(in, "mtime", &v->mtime); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->deserialize_Int(in, "cversion", &v->cversion); rc = rc ? rc : in->deserialize_Int(in, "aversion", &v->aversion); rc = rc ? rc : in->deserialize_Long(in, "ephemeralOwner", &v->ephemeralOwner); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_StatPersistedV1(struct StatPersistedV1*v){ } int serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "protocolVersion", &v->protocolVersion); rc = rc ? rc : out->serialize_Long(out, "lastZxidSeen", &v->lastZxidSeen); rc = rc ? rc : out->serialize_Int(out, "timeOut", &v->timeOut); rc = rc ? rc : out->serialize_Long(out, "sessionId", &v->sessionId); rc = rc ? rc : out->serialize_Buffer(out, "passwd", &v->passwd); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "protocolVersion", &v->protocolVersion); rc = rc ? rc : in->deserialize_Long(in, "lastZxidSeen", &v->lastZxidSeen); rc = rc ? rc : in->deserialize_Int(in, "timeOut", &v->timeOut); rc = rc ? rc : in->deserialize_Long(in, "sessionId", &v->sessionId); rc = rc ? rc : in->deserialize_Buffer(in, "passwd", &v->passwd); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ConnectRequest(struct ConnectRequest*v){ deallocate_Buffer(&v->passwd); } int serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "protocolVersion", &v->protocolVersion); rc = rc ? rc : out->serialize_Int(out, "timeOut", &v->timeOut); rc = rc ? rc : out->serialize_Long(out, "sessionId", &v->sessionId); rc = rc ? rc : out->serialize_Buffer(out, "passwd", &v->passwd); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "protocolVersion", &v->protocolVersion); rc = rc ? rc : in->deserialize_Int(in, "timeOut", &v->timeOut); rc = rc ? rc : in->deserialize_Long(in, "sessionId", &v->sessionId); rc = rc ? rc : in->deserialize_Buffer(in, "passwd", &v->passwd); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ConnectResponse(struct ConnectResponse*v){ deallocate_Buffer(&v->passwd); } int allocate_String_vector(struct String_vector *v, int32_t len) { if (!len) { v->count = 0; v->data = 0; } else { v->count = len; v->data = calloc(sizeof(*v->data), len); } return 0; } int deallocate_String_vector(struct String_vector *v) { if (v->data) { int32_t i; for(i=0;icount; i++) { deallocate_String(&v->data[i]); } free(v->data); v->data = 0; } return 0; } int serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v) { int32_t count = v->count; int rc = 0; int32_t i; rc = out->start_vector(out, tag, &count); for(i=0;icount;i++) { rc = rc ? rc : out->serialize_String(out, "data", &v->data[i]); } rc = rc ? rc : out->end_vector(out, tag); return rc; } int deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v) { int rc = 0; int32_t i; rc = in->start_vector(in, tag, &v->count); v->data = calloc(v->count, sizeof(*v->data)); for(i=0;icount;i++) { rc = rc ? rc : in->deserialize_String(in, "value", &v->data[i]); } rc = in->end_vector(in, tag); return rc; } int serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "relativeZxid", &v->relativeZxid); rc = rc ? rc : serialize_String_vector(out, "dataWatches", &v->dataWatches); rc = rc ? rc : serialize_String_vector(out, "existWatches", &v->existWatches); rc = rc ? rc : serialize_String_vector(out, "childWatches", &v->childWatches); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "relativeZxid", &v->relativeZxid); rc = rc ? rc : deserialize_String_vector(in, "dataWatches", &v->dataWatches); rc = rc ? rc : deserialize_String_vector(in, "existWatches", &v->existWatches); rc = rc ? rc : deserialize_String_vector(in, "childWatches", &v->childWatches); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetWatches(struct SetWatches*v){ deallocate_String_vector(&v->dataWatches); deallocate_String_vector(&v->existWatches); deallocate_String_vector(&v->childWatches); } int serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "xid", &v->xid); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "xid", &v->xid); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_RequestHeader(struct RequestHeader*v){ } int serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->serialize_Bool(out, "done", &v->done); rc = rc ? rc : out->serialize_Int(out, "err", &v->err); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->deserialize_Bool(in, "done", &v->done); rc = rc ? rc : in->deserialize_Int(in, "err", &v->err); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_MultiHeader(struct MultiHeader*v){ } int serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->serialize_String(out, "scheme", &v->scheme); rc = rc ? rc : out->serialize_Buffer(out, "auth", &v->auth); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->deserialize_String(in, "scheme", &v->scheme); rc = rc ? rc : in->deserialize_Buffer(in, "auth", &v->auth); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_AuthPacket(struct AuthPacket*v){ deallocate_String(&v->scheme); deallocate_Buffer(&v->auth); } int serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "xid", &v->xid); rc = rc ? rc : out->serialize_Long(out, "zxid", &v->zxid); rc = rc ? rc : out->serialize_Int(out, "err", &v->err); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "xid", &v->xid); rc = rc ? rc : in->deserialize_Long(in, "zxid", &v->zxid); rc = rc ? rc : in->deserialize_Int(in, "err", &v->err); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ReplyHeader(struct ReplyHeader*v){ } int serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Bool(out, "watch", &v->watch); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Bool(in, "watch", &v->watch); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetDataRequest(struct GetDataRequest*v){ deallocate_String(&v->path); } int serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetDataRequest(struct SetDataRequest*v){ deallocate_String(&v->path); deallocate_Buffer(&v->data); } int serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetDataResponse(struct SetDataResponse*v){ deallocate_Stat(&v->stat); } int serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Buffer(out, "token", &v->token); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Buffer(in, "token", &v->token); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetSASLRequest(struct GetSASLRequest*v){ deallocate_Buffer(&v->token); } int serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Buffer(out, "token", &v->token); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Buffer(in, "token", &v->token); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetSASLRequest(struct SetSASLRequest*v){ deallocate_Buffer(&v->token); } int serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Buffer(out, "token", &v->token); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Buffer(in, "token", &v->token); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetSASLResponse(struct SetSASLResponse*v){ deallocate_Buffer(&v->token); } int allocate_ACL_vector(struct ACL_vector *v, int32_t len) { if (!len) { v->count = 0; v->data = 0; } else { v->count = len; v->data = calloc(sizeof(*v->data), len); } return 0; } int deallocate_ACL_vector(struct ACL_vector *v) { if (v->data) { int32_t i; for(i=0;icount; i++) { deallocate_ACL(&v->data[i]); } free(v->data); v->data = 0; } return 0; } int serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v) { int32_t count = v->count; int rc = 0; int32_t i; rc = out->start_vector(out, tag, &count); for(i=0;icount;i++) { rc = rc ? rc : serialize_ACL(out, "data", &v->data[i]); } rc = rc ? rc : out->end_vector(out, tag); return rc; } int deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v) { int rc = 0; int32_t i; rc = in->start_vector(in, tag, &v->count); v->data = calloc(v->count, sizeof(*v->data)); for(i=0;icount;i++) { rc = rc ? rc : deserialize_ACL(in, "value", &v->data[i]); } rc = in->end_vector(in, tag); return rc; } int serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : out->serialize_Int(out, "flags", &v->flags); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : in->deserialize_Int(in, "flags", &v->flags); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CreateRequest(struct CreateRequest*v){ deallocate_String(&v->path); deallocate_Buffer(&v->data); deallocate_ACL_vector(&v->acl); } int serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_DeleteRequest(struct DeleteRequest*v){ deallocate_String(&v->path); } int serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Bool(out, "watch", &v->watch); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Bool(in, "watch", &v->watch); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetChildrenRequest(struct GetChildrenRequest*v){ deallocate_String(&v->path); } int serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Bool(out, "watch", &v->watch); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Bool(in, "watch", &v->watch); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetChildren2Request(struct GetChildren2Request*v){ deallocate_String(&v->path); } int serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CheckVersionRequest(struct CheckVersionRequest*v){ deallocate_String(&v->path); } int serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*v){ deallocate_String(&v->path); } int serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "max", &v->max); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "max", &v->max); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*v){ } int serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Int(out, "max", &v->max); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Int(in, "max", &v->max); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*v){ deallocate_String(&v->path); } int serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SyncRequest(struct SyncRequest*v){ deallocate_String(&v->path); } int serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SyncResponse(struct SyncResponse*v){ deallocate_String(&v->path); } int serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetACLRequest(struct GetACLRequest*v){ deallocate_String(&v->path); } int serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetACLRequest(struct SetACLRequest*v){ deallocate_String(&v->path); deallocate_ACL_vector(&v->acl); } int serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetACLResponse(struct SetACLResponse*v){ deallocate_Stat(&v->stat); } int serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->serialize_Int(out, "state", &v->state); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->deserialize_Int(in, "state", &v->state); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_WatcherEvent(struct WatcherEvent*v){ deallocate_String(&v->path); } int serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "err", &v->err); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "err", &v->err); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ErrorResponse(struct ErrorResponse*v){ } int serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CreateResponse(struct CreateResponse*v){ deallocate_String(&v->path); } int serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Bool(out, "watch", &v->watch); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Bool(in, "watch", &v->watch); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ExistsRequest(struct ExistsRequest*v){ deallocate_String(&v->path); } int serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ExistsResponse(struct ExistsResponse*v){ deallocate_Stat(&v->stat); } int serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetDataResponse(struct GetDataResponse*v){ deallocate_Buffer(&v->data); deallocate_Stat(&v->stat); } int serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_String_vector(out, "children", &v->children); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_String_vector(in, "children", &v->children); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetChildrenResponse(struct GetChildrenResponse*v){ deallocate_String_vector(&v->children); } int serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_String_vector(out, "children", &v->children); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_String_vector(in, "children", &v->children); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetChildren2Response(struct GetChildren2Response*v){ deallocate_String_vector(&v->children); deallocate_Stat(&v->stat); } int serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : serialize_Stat(out, "stat", &v->stat); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : deserialize_Stat(in, "stat", &v->stat); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_GetACLResponse(struct GetACLResponse*v){ deallocate_ACL_vector(&v->acl); deallocate_Stat(&v->stat); } int serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "serverid", &v->serverid); rc = rc ? rc : out->serialize_Int(out, "protocolVersion", &v->protocolVersion); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "serverid", &v->serverid); rc = rc ? rc : in->deserialize_Int(in, "protocolVersion", &v->protocolVersion); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_LearnerInfo(struct LearnerInfo*v){ } int allocate_Id_vector(struct Id_vector *v, int32_t len) { if (!len) { v->count = 0; v->data = 0; } else { v->count = len; v->data = calloc(sizeof(*v->data), len); } return 0; } int deallocate_Id_vector(struct Id_vector *v) { if (v->data) { int32_t i; for(i=0;icount; i++) { deallocate_Id(&v->data[i]); } free(v->data); v->data = 0; } return 0; } int serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v) { int32_t count = v->count; int rc = 0; int32_t i; rc = out->start_vector(out, tag, &count); for(i=0;icount;i++) { rc = rc ? rc : serialize_Id(out, "data", &v->data[i]); } rc = rc ? rc : out->end_vector(out, tag); return rc; } int deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v) { int rc = 0; int32_t i; rc = in->start_vector(in, tag, &v->count); v->data = calloc(v->count, sizeof(*v->data)); for(i=0;icount;i++) { rc = rc ? rc : deserialize_Id(in, "value", &v->data[i]); } rc = in->end_vector(in, tag); return rc; } int serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->serialize_Long(out, "zxid", &v->zxid); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : serialize_Id_vector(out, "authinfo", &v->authinfo); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->deserialize_Long(in, "zxid", &v->zxid); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : deserialize_Id_vector(in, "authinfo", &v->authinfo); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_QuorumPacket(struct QuorumPacket*v){ deallocate_Buffer(&v->data); deallocate_Id_vector(&v->authinfo); } int serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "magic", &v->magic); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->serialize_Long(out, "dbid", &v->dbid); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "magic", &v->magic); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->deserialize_Long(in, "dbid", &v->dbid); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_FileHeader(struct FileHeader*v){ } int serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Long(out, "clientId", &v->clientId); rc = rc ? rc : out->serialize_Int(out, "cxid", &v->cxid); rc = rc ? rc : out->serialize_Long(out, "zxid", &v->zxid); rc = rc ? rc : out->serialize_Long(out, "time", &v->time); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Long(in, "clientId", &v->clientId); rc = rc ? rc : in->deserialize_Int(in, "cxid", &v->cxid); rc = rc ? rc : in->deserialize_Long(in, "zxid", &v->zxid); rc = rc ? rc : in->deserialize_Long(in, "time", &v->time); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_TxnHeader(struct TxnHeader*v){ } int serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : out->serialize_Bool(out, "ephemeral", &v->ephemeral); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : in->deserialize_Bool(in, "ephemeral", &v->ephemeral); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CreateTxnV0(struct CreateTxnV0*v){ deallocate_String(&v->path); deallocate_Buffer(&v->data); deallocate_ACL_vector(&v->acl); } int serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : out->serialize_Bool(out, "ephemeral", &v->ephemeral); rc = rc ? rc : out->serialize_Int(out, "parentCVersion", &v->parentCVersion); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : in->deserialize_Bool(in, "ephemeral", &v->ephemeral); rc = rc ? rc : in->deserialize_Int(in, "parentCVersion", &v->parentCVersion); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CreateTxn(struct CreateTxn*v){ deallocate_String(&v->path); deallocate_Buffer(&v->data); deallocate_ACL_vector(&v->acl); } int serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_DeleteTxn(struct DeleteTxn*v){ deallocate_String(&v->path); } int serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetDataTxn(struct SetDataTxn*v){ deallocate_String(&v->path); deallocate_Buffer(&v->data); } int serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CheckVersionTxn(struct CheckVersionTxn*v){ deallocate_String(&v->path); } int serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : serialize_ACL_vector(out, "acl", &v->acl); rc = rc ? rc : out->serialize_Int(out, "version", &v->version); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : deserialize_ACL_vector(in, "acl", &v->acl); rc = rc ? rc : in->deserialize_Int(in, "version", &v->version); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetACLTxn(struct SetACLTxn*v){ deallocate_String(&v->path); deallocate_ACL_vector(&v->acl); } int serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_String(out, "path", &v->path); rc = rc ? rc : out->serialize_Int(out, "max", &v->max); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_String(in, "path", &v->path); rc = rc ? rc : in->deserialize_Int(in, "max", &v->max); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*v){ deallocate_String(&v->path); } int serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "timeOut", &v->timeOut); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "timeOut", &v->timeOut); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_CreateSessionTxn(struct CreateSessionTxn*v){ } int serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "err", &v->err); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "err", &v->err); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_ErrorTxn(struct ErrorTxn*v){ } int serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : out->serialize_Int(out, "type", &v->type); rc = rc ? rc : out->serialize_Buffer(out, "data", &v->data); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : in->deserialize_Int(in, "type", &v->type); rc = rc ? rc : in->deserialize_Buffer(in, "data", &v->data); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_Txn(struct Txn*v){ deallocate_Buffer(&v->data); } int allocate_Txn_vector(struct Txn_vector *v, int32_t len) { if (!len) { v->count = 0; v->data = 0; } else { v->count = len; v->data = calloc(sizeof(*v->data), len); } return 0; } int deallocate_Txn_vector(struct Txn_vector *v) { if (v->data) { int32_t i; for(i=0;icount; i++) { deallocate_Txn(&v->data[i]); } free(v->data); v->data = 0; } return 0; } int serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v) { int32_t count = v->count; int rc = 0; int32_t i; rc = out->start_vector(out, tag, &count); for(i=0;icount;i++) { rc = rc ? rc : serialize_Txn(out, "data", &v->data[i]); } rc = rc ? rc : out->end_vector(out, tag); return rc; } int deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v) { int rc = 0; int32_t i; rc = in->start_vector(in, tag, &v->count); v->data = calloc(v->count, sizeof(*v->data)); for(i=0;icount;i++) { rc = rc ? rc : deserialize_Txn(in, "value", &v->data[i]); } rc = in->end_vector(in, tag); return rc; } int serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v){ int rc; rc = out->start_record(out, tag); rc = rc ? rc : serialize_Txn_vector(out, "txns", &v->txns); rc = rc ? rc : out->end_record(out, tag); return rc; } int deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v){ int rc; rc = in->start_record(in, tag); rc = rc ? rc : deserialize_Txn_vector(in, "txns", &v->txns); rc = rc ? rc : in->end_record(in, tag); return rc; } void deallocate_MultiTxn(struct MultiTxn*v){ deallocate_Txn_vector(&v->txns); } ================================================ FILE: QSentinel/zookeeper/zookeeper.jute.h ================================================ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ZOOKEEPER_JUTE__ #define __ZOOKEEPER_JUTE__ #include "recordio.h" #ifdef __cplusplus extern "C" { #endif struct Id { char * scheme; char * id; }; int serialize_Id(struct oarchive *out, const char *tag, struct Id *v); int deserialize_Id(struct iarchive *in, const char *tag, struct Id*v); void deallocate_Id(struct Id*); struct ACL { int32_t perms; struct Id id; }; int serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v); int deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v); void deallocate_ACL(struct ACL*); struct Stat { int64_t czxid; int64_t mzxid; int64_t ctime; int64_t mtime; int32_t version; int32_t cversion; int32_t aversion; int64_t ephemeralOwner; int32_t dataLength; int32_t numChildren; int64_t pzxid; }; int serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v); int deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v); void deallocate_Stat(struct Stat*); struct StatPersisted { int64_t czxid; int64_t mzxid; int64_t ctime; int64_t mtime; int32_t version; int32_t cversion; int32_t aversion; int64_t ephemeralOwner; int64_t pzxid; }; int serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v); int deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v); void deallocate_StatPersisted(struct StatPersisted*); struct StatPersistedV1 { int64_t czxid; int64_t mzxid; int64_t ctime; int64_t mtime; int32_t version; int32_t cversion; int32_t aversion; int64_t ephemeralOwner; }; int serialize_StatPersistedV1(struct oarchive *out, const char *tag, struct StatPersistedV1 *v); int deserialize_StatPersistedV1(struct iarchive *in, const char *tag, struct StatPersistedV1*v); void deallocate_StatPersistedV1(struct StatPersistedV1*); struct ConnectRequest { int32_t protocolVersion; int64_t lastZxidSeen; int32_t timeOut; int64_t sessionId; struct buffer passwd; }; int serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v); int deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v); void deallocate_ConnectRequest(struct ConnectRequest*); struct ConnectResponse { int32_t protocolVersion; int32_t timeOut; int64_t sessionId; struct buffer passwd; }; int serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v); int deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v); void deallocate_ConnectResponse(struct ConnectResponse*); struct String_vector { int32_t count; char * *data; }; int serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v); int deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v); int allocate_String_vector(struct String_vector *v, int32_t len); int deallocate_String_vector(struct String_vector *v); struct SetWatches { int64_t relativeZxid; struct String_vector dataWatches; struct String_vector existWatches; struct String_vector childWatches; }; int serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v); int deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v); void deallocate_SetWatches(struct SetWatches*); struct RequestHeader { int32_t xid; int32_t type; }; int serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v); int deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v); void deallocate_RequestHeader(struct RequestHeader*); struct MultiHeader { int32_t type; int32_t done; int32_t err; }; int serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v); int deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v); void deallocate_MultiHeader(struct MultiHeader*); struct AuthPacket { int32_t type; char * scheme; struct buffer auth; }; int serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v); int deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v); void deallocate_AuthPacket(struct AuthPacket*); struct ReplyHeader { int32_t xid; int64_t zxid; int32_t err; }; int serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v); int deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v); void deallocate_ReplyHeader(struct ReplyHeader*); struct GetDataRequest { char * path; int32_t watch; }; int serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v); int deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v); void deallocate_GetDataRequest(struct GetDataRequest*); struct SetDataRequest { char * path; struct buffer data; int32_t version; }; int serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v); int deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v); void deallocate_SetDataRequest(struct SetDataRequest*); struct SetDataResponse { struct Stat stat; }; int serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v); int deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v); void deallocate_SetDataResponse(struct SetDataResponse*); struct GetSASLRequest { struct buffer token; }; int serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v); int deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v); void deallocate_GetSASLRequest(struct GetSASLRequest*); struct SetSASLRequest { struct buffer token; }; int serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v); int deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v); void deallocate_SetSASLRequest(struct SetSASLRequest*); struct SetSASLResponse { struct buffer token; }; int serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v); int deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v); void deallocate_SetSASLResponse(struct SetSASLResponse*); struct ACL_vector { int32_t count; struct ACL *data; }; int serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v); int deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v); int allocate_ACL_vector(struct ACL_vector *v, int32_t len); int deallocate_ACL_vector(struct ACL_vector *v); struct CreateRequest { char * path; struct buffer data; struct ACL_vector acl; int32_t flags; }; int serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v); int deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v); void deallocate_CreateRequest(struct CreateRequest*); struct DeleteRequest { char * path; int32_t version; }; int serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v); int deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v); void deallocate_DeleteRequest(struct DeleteRequest*); struct GetChildrenRequest { char * path; int32_t watch; }; int serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v); int deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v); void deallocate_GetChildrenRequest(struct GetChildrenRequest*); struct GetChildren2Request { char * path; int32_t watch; }; int serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v); int deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v); void deallocate_GetChildren2Request(struct GetChildren2Request*); struct CheckVersionRequest { char * path; int32_t version; }; int serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v); int deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v); void deallocate_CheckVersionRequest(struct CheckVersionRequest*); struct GetMaxChildrenRequest { char * path; }; int serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v); int deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v); void deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*); struct GetMaxChildrenResponse { int32_t max; }; int serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v); int deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v); void deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*); struct SetMaxChildrenRequest { char * path; int32_t max; }; int serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v); int deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v); void deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*); struct SyncRequest { char * path; }; int serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v); int deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v); void deallocate_SyncRequest(struct SyncRequest*); struct SyncResponse { char * path; }; int serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v); int deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v); void deallocate_SyncResponse(struct SyncResponse*); struct GetACLRequest { char * path; }; int serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v); int deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v); void deallocate_GetACLRequest(struct GetACLRequest*); struct SetACLRequest { char * path; struct ACL_vector acl; int32_t version; }; int serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v); int deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v); void deallocate_SetACLRequest(struct SetACLRequest*); struct SetACLResponse { struct Stat stat; }; int serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v); int deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v); void deallocate_SetACLResponse(struct SetACLResponse*); struct WatcherEvent { int32_t type; int32_t state; char * path; }; int serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v); int deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v); void deallocate_WatcherEvent(struct WatcherEvent*); struct ErrorResponse { int32_t err; }; int serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v); int deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v); void deallocate_ErrorResponse(struct ErrorResponse*); struct CreateResponse { char * path; }; int serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v); int deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v); void deallocate_CreateResponse(struct CreateResponse*); struct ExistsRequest { char * path; int32_t watch; }; int serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v); int deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v); void deallocate_ExistsRequest(struct ExistsRequest*); struct ExistsResponse { struct Stat stat; }; int serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v); int deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v); void deallocate_ExistsResponse(struct ExistsResponse*); struct GetDataResponse { struct buffer data; struct Stat stat; }; int serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v); int deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v); void deallocate_GetDataResponse(struct GetDataResponse*); struct GetChildrenResponse { struct String_vector children; }; int serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v); int deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v); void deallocate_GetChildrenResponse(struct GetChildrenResponse*); struct GetChildren2Response { struct String_vector children; struct Stat stat; }; int serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v); int deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v); void deallocate_GetChildren2Response(struct GetChildren2Response*); struct GetACLResponse { struct ACL_vector acl; struct Stat stat; }; int serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v); int deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v); void deallocate_GetACLResponse(struct GetACLResponse*); struct LearnerInfo { int64_t serverid; int32_t protocolVersion; }; int serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v); int deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v); void deallocate_LearnerInfo(struct LearnerInfo*); struct Id_vector { int32_t count; struct Id *data; }; int serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v); int deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v); int allocate_Id_vector(struct Id_vector *v, int32_t len); int deallocate_Id_vector(struct Id_vector *v); struct QuorumPacket { int32_t type; int64_t zxid; struct buffer data; struct Id_vector authinfo; }; int serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v); int deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v); void deallocate_QuorumPacket(struct QuorumPacket*); struct FileHeader { int32_t magic; int32_t version; int64_t dbid; }; int serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v); int deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v); void deallocate_FileHeader(struct FileHeader*); struct TxnHeader { int64_t clientId; int32_t cxid; int64_t zxid; int64_t time; int32_t type; }; int serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v); int deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v); void deallocate_TxnHeader(struct TxnHeader*); struct CreateTxnV0 { char * path; struct buffer data; struct ACL_vector acl; int32_t ephemeral; }; int serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v); int deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v); void deallocate_CreateTxnV0(struct CreateTxnV0*); struct CreateTxn { char * path; struct buffer data; struct ACL_vector acl; int32_t ephemeral; int32_t parentCVersion; }; int serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v); int deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v); void deallocate_CreateTxn(struct CreateTxn*); struct DeleteTxn { char * path; }; int serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v); int deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v); void deallocate_DeleteTxn(struct DeleteTxn*); struct SetDataTxn { char * path; struct buffer data; int32_t version; }; int serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v); int deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v); void deallocate_SetDataTxn(struct SetDataTxn*); struct CheckVersionTxn { char * path; int32_t version; }; int serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v); int deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v); void deallocate_CheckVersionTxn(struct CheckVersionTxn*); struct SetACLTxn { char * path; struct ACL_vector acl; int32_t version; }; int serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v); int deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v); void deallocate_SetACLTxn(struct SetACLTxn*); struct SetMaxChildrenTxn { char * path; int32_t max; }; int serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v); int deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v); void deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*); struct CreateSessionTxn { int32_t timeOut; }; int serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v); int deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v); void deallocate_CreateSessionTxn(struct CreateSessionTxn*); struct ErrorTxn { int32_t err; }; int serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v); int deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v); void deallocate_ErrorTxn(struct ErrorTxn*); struct Txn { int32_t type; struct buffer data; }; int serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v); int deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v); void deallocate_Txn(struct Txn*); struct Txn_vector { int32_t count; struct Txn *data; }; int serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v); int deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v); int allocate_Txn_vector(struct Txn_vector *v, int32_t len); int deallocate_Txn_vector(struct Txn_vector *v); struct MultiTxn { struct Txn_vector txns; }; int serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v); int deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v); void deallocate_MultiTxn(struct MultiTxn*); #ifdef __cplusplus } #endif #endif //ZOOKEEPER_JUTE__ ================================================ FILE: QSentinel/zookeeper/zookeeper_version.h ================================================ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ZOOKEEPER_VERSION_H_ #define ZOOKEEPER_VERSION_H_ #ifdef __cplusplus extern "C" { #endif #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 #define ZOO_PATCH_VERSION 6 #ifdef __cplusplus } #endif #endif /* ZOOKEEPER_VERSION_H_ */ ================================================ FILE: Qedis.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ BA1250711A6D43D500A0532D /* MemoryFile.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA12506F1A6D43D500A0532D /* MemoryFile.cc */; }; BA1250721A6D43D500A0532D /* MemoryFile.h in Headers */ = {isa = PBXBuildFile; fileRef = BA1250701A6D43D500A0532D /* MemoryFile.h */; }; BA1250811A7338CD00A0532D /* AsyncBuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA12507F1A7338CD00A0532D /* AsyncBuffer.cc */; }; BA1250821A7338CD00A0532D /* AsyncBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA1250801A7338CD00A0532D /* AsyncBuffer.h */; }; BA29C44E19FB2CEE00796E3A /* QMulti.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA29C44C19FB2CEE00796E3A /* QMulti.cc */; }; BA29C44F19FB2CEE00796E3A /* QMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = BA29C44D19FB2CEE00796E3A /* QMulti.h */; }; BA2C0C7B1CDC1DC800BDF050 /* Delegate.h in Headers */ = {isa = PBXBuildFile; fileRef = BA2C0C7A1CDC1DC800BDF050 /* Delegate.h */; }; BA34F9741D5C199D00F1DE01 /* builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E31D5C199D00F1DE01 /* builder.cc */; }; BA34F9751D5C199D00F1DE01 /* builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8E41D5C199D00F1DE01 /* builder.h */; }; BA34F9761D5C199D00F1DE01 /* c.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E51D5C199D00F1DE01 /* c.cc */; }; BA34F9791D5C199D00F1DE01 /* db_bench.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E81D5C199D00F1DE01 /* db_bench.cc */; }; BA34F97A1D5C199D00F1DE01 /* db_impl.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8E91D5C199D00F1DE01 /* db_impl.cc */; }; BA34F97B1D5C199D00F1DE01 /* db_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EA1D5C199D00F1DE01 /* db_impl.h */; }; BA34F97C1D5C199D00F1DE01 /* db_iter.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8EB1D5C199D00F1DE01 /* db_iter.cc */; }; BA34F97D1D5C199D00F1DE01 /* db_iter.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EC1D5C199D00F1DE01 /* db_iter.h */; }; BA34F97F1D5C199D00F1DE01 /* dbformat.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8EE1D5C199D00F1DE01 /* dbformat.cc */; }; BA34F9801D5C199D00F1DE01 /* dbformat.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8EF1D5C199D00F1DE01 /* dbformat.h */; }; BA34F9821D5C199D00F1DE01 /* dumpfile.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F11D5C199D00F1DE01 /* dumpfile.cc */; }; BA34F9841D5C199D00F1DE01 /* filename.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F31D5C199D00F1DE01 /* filename.cc */; }; BA34F9851D5C199D00F1DE01 /* filename.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F41D5C199D00F1DE01 /* filename.h */; }; BA34F9881D5C199D00F1DE01 /* log_format.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F71D5C199D00F1DE01 /* log_format.h */; }; BA34F9891D5C199D00F1DE01 /* log_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8F81D5C199D00F1DE01 /* log_reader.cc */; }; BA34F98A1D5C199D00F1DE01 /* log_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8F91D5C199D00F1DE01 /* log_reader.h */; }; BA34F98C1D5C199D00F1DE01 /* log_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FB1D5C199D00F1DE01 /* log_writer.cc */; }; BA34F98D1D5C199D00F1DE01 /* log_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8FC1D5C199D00F1DE01 /* log_writer.h */; }; BA34F98E1D5C199D00F1DE01 /* memtable.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FD1D5C199D00F1DE01 /* memtable.cc */; }; BA34F98F1D5C199D00F1DE01 /* memtable.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F8FE1D5C199D00F1DE01 /* memtable.h */; }; BA34F9901D5C199D00F1DE01 /* repair.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F8FF1D5C199D00F1DE01 /* repair.cc */; }; BA34F9911D5C199D00F1DE01 /* skiplist.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9001D5C199D00F1DE01 /* skiplist.h */; }; BA34F9931D5C199D00F1DE01 /* snapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9021D5C199D00F1DE01 /* snapshot.h */; }; BA34F9941D5C199D00F1DE01 /* table_cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9031D5C199D00F1DE01 /* table_cache.cc */; }; BA34F9951D5C199D00F1DE01 /* table_cache.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9041D5C199D00F1DE01 /* table_cache.h */; }; BA34F9961D5C199D00F1DE01 /* version_edit.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9051D5C199D00F1DE01 /* version_edit.cc */; }; BA34F9971D5C199D00F1DE01 /* version_edit.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9061D5C199D00F1DE01 /* version_edit.h */; }; BA34F9991D5C199D00F1DE01 /* version_set.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9081D5C199D00F1DE01 /* version_set.cc */; }; BA34F99A1D5C199D00F1DE01 /* version_set.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9091D5C199D00F1DE01 /* version_set.h */; }; BA34F99C1D5C199D00F1DE01 /* write_batch.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F90B1D5C199D00F1DE01 /* write_batch.cc */; }; BA34F99D1D5C199D00F1DE01 /* write_batch_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */; }; BA34F9A11D5C199D00F1DE01 /* memenv.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F91A1D5C199D00F1DE01 /* memenv.cc */; }; BA34F9A21D5C199D00F1DE01 /* memenv.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F91B1D5C199D00F1DE01 /* memenv.h */; }; BA34F9A41D5C199D00F1DE01 /* c.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F91F1D5C199D00F1DE01 /* c.h */; }; BA34F9A51D5C199D00F1DE01 /* cache.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9201D5C199D00F1DE01 /* cache.h */; }; BA34F9A61D5C199D00F1DE01 /* comparator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9211D5C199D00F1DE01 /* comparator.h */; }; BA34F9A71D5C199D00F1DE01 /* db.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9221D5C199D00F1DE01 /* db.h */; }; BA34F9A81D5C199D00F1DE01 /* dumpfile.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9231D5C199D00F1DE01 /* dumpfile.h */; }; BA34F9A91D5C199D00F1DE01 /* env.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9241D5C199D00F1DE01 /* env.h */; }; BA34F9AA1D5C199D00F1DE01 /* filter_policy.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9251D5C199D00F1DE01 /* filter_policy.h */; }; BA34F9AB1D5C199D00F1DE01 /* iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9261D5C199D00F1DE01 /* iterator.h */; }; BA34F9AC1D5C199D00F1DE01 /* options.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9271D5C199D00F1DE01 /* options.h */; }; BA34F9AD1D5C199D00F1DE01 /* slice.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9281D5C199D00F1DE01 /* slice.h */; }; BA34F9AE1D5C199D00F1DE01 /* status.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9291D5C199D00F1DE01 /* status.h */; }; BA34F9AF1D5C199D00F1DE01 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92A1D5C199D00F1DE01 /* table.h */; }; BA34F9B01D5C199D00F1DE01 /* table_builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92B1D5C199D00F1DE01 /* table_builder.h */; }; BA34F9B11D5C199D00F1DE01 /* write_batch.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F92C1D5C199D00F1DE01 /* write_batch.h */; }; BA34F9B51D5C199D00F1DE01 /* atomic_pointer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9321D5C199D00F1DE01 /* atomic_pointer.h */; }; BA34F9B61D5C199D00F1DE01 /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9331D5C199D00F1DE01 /* port.h */; }; BA34F9B71D5C199D00F1DE01 /* port_example.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9341D5C199D00F1DE01 /* port_example.h */; }; BA34F9B81D5C199D00F1DE01 /* port_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9351D5C199D00F1DE01 /* port_posix.cc */; }; BA34F9B91D5C199D00F1DE01 /* port_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9361D5C199D00F1DE01 /* port_posix.h */; }; BA34F9BA1D5C199D00F1DE01 /* thread_annotations.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9381D5C199D00F1DE01 /* thread_annotations.h */; }; BA34F9BD1D5C199D00F1DE01 /* block.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F93D1D5C199D00F1DE01 /* block.cc */; }; BA34F9BE1D5C199D00F1DE01 /* block.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F93E1D5C199D00F1DE01 /* block.h */; }; BA34F9BF1D5C199D00F1DE01 /* block_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F93F1D5C199D00F1DE01 /* block_builder.cc */; }; BA34F9C01D5C199D00F1DE01 /* block_builder.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9401D5C199D00F1DE01 /* block_builder.h */; }; BA34F9C11D5C199D00F1DE01 /* filter_block.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9411D5C199D00F1DE01 /* filter_block.cc */; }; BA34F9C21D5C199D00F1DE01 /* filter_block.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9421D5C199D00F1DE01 /* filter_block.h */; }; BA34F9C41D5C199D00F1DE01 /* format.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9441D5C199D00F1DE01 /* format.cc */; }; BA34F9C51D5C199D00F1DE01 /* format.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9451D5C199D00F1DE01 /* format.h */; }; BA34F9C61D5C199D00F1DE01 /* iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9461D5C199D00F1DE01 /* iterator.cc */; }; BA34F9C71D5C199D00F1DE01 /* iterator_wrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */; }; BA34F9C81D5C199D00F1DE01 /* merger.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9481D5C199D00F1DE01 /* merger.cc */; }; BA34F9C91D5C199D00F1DE01 /* merger.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9491D5C199D00F1DE01 /* merger.h */; }; BA34F9CA1D5C199D00F1DE01 /* table.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94A1D5C199D00F1DE01 /* table.cc */; }; BA34F9CB1D5C199D00F1DE01 /* table_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94B1D5C199D00F1DE01 /* table_builder.cc */; }; BA34F9CD1D5C199D00F1DE01 /* two_level_iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */; }; BA34F9CE1D5C199D00F1DE01 /* two_level_iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */; }; BA34F9CF1D5C199D00F1DE01 /* arena.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9511D5C199D00F1DE01 /* arena.cc */; }; BA34F9D01D5C199D00F1DE01 /* arena.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9521D5C199D00F1DE01 /* arena.h */; }; BA34F9D21D5C199D00F1DE01 /* bloom.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9541D5C199D00F1DE01 /* bloom.cc */; }; BA34F9D41D5C199D00F1DE01 /* cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9561D5C199D00F1DE01 /* cache.cc */; }; BA34F9D61D5C199D00F1DE01 /* coding.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9581D5C199D00F1DE01 /* coding.cc */; }; BA34F9D71D5C199D00F1DE01 /* coding.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9591D5C199D00F1DE01 /* coding.h */; }; BA34F9D91D5C199D00F1DE01 /* comparator.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95B1D5C199D00F1DE01 /* comparator.cc */; }; BA34F9DA1D5C199D00F1DE01 /* crc32c.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95C1D5C199D00F1DE01 /* crc32c.cc */; }; BA34F9DB1D5C199D00F1DE01 /* crc32c.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F95D1D5C199D00F1DE01 /* crc32c.h */; }; BA34F9DD1D5C199D00F1DE01 /* env.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F95F1D5C199D00F1DE01 /* env.cc */; }; BA34F9DE1D5C199D00F1DE01 /* env_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9601D5C199D00F1DE01 /* env_posix.cc */; }; BA34F9E01D5C199D00F1DE01 /* filter_policy.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9621D5C199D00F1DE01 /* filter_policy.cc */; }; BA34F9E11D5C199D00F1DE01 /* hash.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9631D5C199D00F1DE01 /* hash.cc */; }; BA34F9E21D5C199D00F1DE01 /* hash.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9641D5C199D00F1DE01 /* hash.h */; }; BA34F9E41D5C199D00F1DE01 /* histogram.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9661D5C199D00F1DE01 /* histogram.cc */; }; BA34F9E51D5C199D00F1DE01 /* histogram.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9671D5C199D00F1DE01 /* histogram.h */; }; BA34F9E61D5C199D00F1DE01 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9681D5C199D00F1DE01 /* logging.cc */; }; BA34F9E71D5C199D00F1DE01 /* logging.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9691D5C199D00F1DE01 /* logging.h */; }; BA34F9E81D5C199D00F1DE01 /* mutexlock.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96A1D5C199D00F1DE01 /* mutexlock.h */; }; BA34F9E91D5C199D00F1DE01 /* options.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96B1D5C199D00F1DE01 /* options.cc */; }; BA34F9EA1D5C199D00F1DE01 /* posix_logger.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96C1D5C199D00F1DE01 /* posix_logger.h */; }; BA34F9EB1D5C199D00F1DE01 /* random.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F96D1D5C199D00F1DE01 /* random.h */; }; BA34F9EC1D5C199D00F1DE01 /* status.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96E1D5C199D00F1DE01 /* status.cc */; }; BA34F9ED1D5C199D00F1DE01 /* testharness.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F96F1D5C199D00F1DE01 /* testharness.cc */; }; BA34F9EE1D5C199D00F1DE01 /* testharness.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9701D5C199D00F1DE01 /* testharness.h */; }; BA34F9EF1D5C199D00F1DE01 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9711D5C199D00F1DE01 /* testutil.cc */; }; BA34F9F01D5C199D00F1DE01 /* testutil.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9721D5C199D00F1DE01 /* testutil.h */; }; BA34F9F21D5C221700F1DE01 /* libleveldb.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */; settings = {ATTRIBUTES = (Required, ); }; }; BA34F9F31D5C243800F1DE01 /* libqedislib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */; }; BA34F9F41D5C244E00F1DE01 /* libqbaselib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */; settings = {ATTRIBUTES = (Required, ); }; }; BA34F9F81D5C25E400F1DE01 /* QDumpInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */; }; BA34F9F91D5C25E400F1DE01 /* QLeveldb.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */; }; BA34F9FA1D5C25E400F1DE01 /* QLeveldb.h in Headers */ = {isa = PBXBuildFile; fileRef = BA34F9F71D5C25E400F1DE01 /* QLeveldb.h */; }; BA34F9FB1D5C265D00F1DE01 /* libqbaselib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */; }; BA34F9FC1D5C266200F1DE01 /* libleveldb.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */; }; BA3B0AD61ACBA9E1000D9399 /* QAOF.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA3B0AD41ACBA9E1000D9399 /* QAOF.cc */; }; BA3B0AD71ACBA9E1000D9399 /* QAOF.h in Headers */ = {isa = PBXBuildFile; fileRef = BA3B0AD51ACBA9E1000D9399 /* QAOF.h */; }; BA4053931BA1AE3B00B93053 /* QReplication.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4053911BA1AE3B00B93053 /* QReplication.cc */; }; BA4053941BA1AE3B00B93053 /* QReplication.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4053921BA1AE3B00B93053 /* QReplication.h */; }; BA47763D1AB0773F00EDBB5D /* redisIntset.c in Sources */ = {isa = PBXBuildFile; fileRef = BA47763B1AB0773F00EDBB5D /* redisIntset.c */; }; BA47763E1AB0773F00EDBB5D /* redisIntset.h in Headers */ = {isa = PBXBuildFile; fileRef = BA47763C1AB0773F00EDBB5D /* redisIntset.h */; }; BA4CD2261CEB54FF00F68F67 /* QHashModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */; }; BA4CD2271CEB54FF00F68F67 /* QHashModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD21F1CEB54FF00F68F67 /* QHashModule.h */; }; BA4CD2281CEB54FF00F68F67 /* QListModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2201CEB54FF00F68F67 /* QListModule.cc */; }; BA4CD2291CEB54FF00F68F67 /* QListModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2211CEB54FF00F68F67 /* QListModule.h */; }; BA4CD22A1CEB54FF00F68F67 /* QModuleInit.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */; }; BA4CD22B1CEB54FF00F68F67 /* QModuleInit.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2231CEB54FF00F68F67 /* QModuleInit.h */; }; BA4CD22C1CEB54FF00F68F67 /* QSetModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2241CEB54FF00F68F67 /* QSetModule.cc */; }; BA4CD22D1CEB54FF00F68F67 /* QSetModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2251CEB54FF00F68F67 /* QSetModule.h */; }; BA4CD2321CEB61E100F68F67 /* QModule.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CD2301CEB61E100F68F67 /* QModule.cc */; }; BA4CD2331CEB61E100F68F67 /* QModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA4CD2311CEB61E100F68F67 /* QModule.h */; }; BA6C336C19EA77B200AC1800 /* QGlobRegex.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6C336A19EA77B200AC1800 /* QGlobRegex.cc */; }; BA6C336D19EA77B200AC1800 /* QGlobRegex.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6C336B19EA77B200AC1800 /* QGlobRegex.h */; }; BA6C336F19EA77C100AC1800 /* QGlobRegex_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */; }; BA6C337119EC0D9900AC1800 /* QPubsub.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6C337019EC0D9900AC1800 /* QPubsub.h */; }; BA6E478E1F99056000EFBE6F /* QSlaveClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E478C1F99056000EFBE6F /* QSlaveClient.cc */; }; BA6E47951F9905E600EFBE6F /* QClusterClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47921F9905E600EFBE6F /* QClusterClient.cc */; }; BA6E479E1F9905F500EFBE6F /* recordio.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47971F9905F500EFBE6F /* recordio.c */; }; BA6E479F1F9905F500EFBE6F /* zookeeper.jute.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */; }; BA6E47A01F9905F500EFBE6F /* ZookeeperConn.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */; }; BA6E47AD1F99068A00EFBE6F /* QClusterClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47921F9905E600EFBE6F /* QClusterClient.cc */; }; BA6E47AE1F99068A00EFBE6F /* recordio.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E47971F9905F500EFBE6F /* recordio.c */; }; BA6E47AF1F99068A00EFBE6F /* zookeeper.jute.c in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */; }; BA6E47B01F99068A00EFBE6F /* ZookeeperConn.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */; }; BA6E47B11F9906A400EFBE6F /* QClusterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47931F9905E600EFBE6F /* QClusterClient.h */; }; BA6E47B21F9906A400EFBE6F /* QClusterInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47941F9905E600EFBE6F /* QClusterInterface.h */; }; BA6E47B31F9906A400EFBE6F /* proto.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47961F9905F500EFBE6F /* proto.h */; settings = {ATTRIBUTES = (Private, ); }; }; BA6E47B41F9906A400EFBE6F /* recordio.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47981F9905F500EFBE6F /* recordio.h */; settings = {ATTRIBUTES = (Private, ); }; }; BA6E47B51F9906A400EFBE6F /* zookeeper_version.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E47991F9905F500EFBE6F /* zookeeper_version.h */; settings = {ATTRIBUTES = (Private, ); }; }; BA6E47B61F9906A400EFBE6F /* zookeeper.jute.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */; settings = {ATTRIBUTES = (Private, ); }; }; BA6E47B71F9906A400EFBE6F /* ZookeeperConn.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */; settings = {ATTRIBUTES = (Private, ); }; }; BA98F83D1B01B16600ABCBD9 /* QConfig.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA98F83B1B01B16600ABCBD9 /* QConfig.cc */; }; BA98F83E1B01B16600ABCBD9 /* QConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = BA98F83C1B01B16600ABCBD9 /* QConfig.h */; }; BA98F8411B1B539600ABCBD9 /* QSlowLog.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */; }; BA98F8421B1B539600ABCBD9 /* QSlowLog.h in Headers */ = {isa = PBXBuildFile; fileRef = BA98F8401B1B539600ABCBD9 /* QSlowLog.h */; }; BAB2265E2007B7B7005241F6 /* QMigration.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAB2265C2007B7B7005241F6 /* QMigration.cc */; }; BAD0C5FF19E7A02C005B3784 /* QClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E319E7A02C005B3784 /* QClient.cc */; }; BAD0C60019E7A02C005B3784 /* QClient.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E419E7A02C005B3784 /* QClient.h */; }; BAD0C60119E7A02C005B3784 /* QCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E519E7A02C005B3784 /* QCommand.cc */; }; BAD0C60219E7A02C005B3784 /* QCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E619E7A02C005B3784 /* QCommand.h */; }; BAD0C60319E7A02C005B3784 /* QCommon.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5E719E7A02C005B3784 /* QCommon.cc */; }; BAD0C60419E7A02C005B3784 /* QCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5E819E7A02C005B3784 /* QCommon.h */; }; BAD0C60519E7A02C005B3784 /* QHash.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EA19E7A02C005B3784 /* QHash.cc */; }; BAD0C60619E7A02C005B3784 /* QHash.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5EB19E7A02C005B3784 /* QHash.h */; }; BAD0C60719E7A02C005B3784 /* QKeyCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EC19E7A02C005B3784 /* QKeyCommand.cc */; }; BAD0C60819E7A02C005B3784 /* QList.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5ED19E7A02C005B3784 /* QList.cc */; }; BAD0C60919E7A02C005B3784 /* QList.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5EE19E7A02C005B3784 /* QList.h */; }; BAD0C60A19E7A02C005B3784 /* QPubsub.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5EF19E7A02C005B3784 /* QPubsub.cc */; }; BAD0C60B19E7A02C005B3784 /* QServerCommand.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F019E7A02C005B3784 /* QServerCommand.cc */; }; BAD0C60C19E7A02C005B3784 /* QSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F119E7A02C005B3784 /* QSet.cc */; }; BAD0C60D19E7A02C005B3784 /* QSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F219E7A02C005B3784 /* QSet.h */; }; BAD0C60E19E7A02C005B3784 /* QSortedSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F319E7A02C005B3784 /* QSortedSet.cc */; }; BAD0C60F19E7A02C005B3784 /* QSortedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F419E7A02C005B3784 /* QSortedSet.h */; }; BAD0C61219E7A02C005B3784 /* QStore.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F719E7A02C005B3784 /* QStore.cc */; }; BAD0C61319E7A02C005B3784 /* QStore.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5F819E7A02C005B3784 /* QStore.h */; }; BAD0C61419E7A02C005B3784 /* QString.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5F919E7A02C005B3784 /* QString.cc */; }; BAD0C61519E7A02C005B3784 /* QString.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5FA19E7A02C005B3784 /* QString.h */; }; BAD0C61619E7A02C005B3784 /* QHelper.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C5FB19E7A02C005B3784 /* QHelper.cc */; }; BAD0C61719E7A02C005B3784 /* QHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BAD0C5FC19E7A02C005B3784 /* QHelper.h */; }; BAD0C61A19E7A075005B3784 /* Qedis.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAD0C61919E7A075005B3784 /* Qedis.cc */; }; BAD0C62519E7A8DA005B3784 /* libqedislib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */; }; BADC7D7C19D6BC960036858E /* UnitTest.cc in Sources */ = {isa = PBXBuildFile; fileRef = BADC7D7A19D6BC960036858E /* UnitTest.cc */; }; BADD49B51CCA68BA0093257E /* QProtoParser.cc in Sources */ = {isa = PBXBuildFile; fileRef = BADD49B31CCA68BA0093257E /* QProtoParser.cc */; }; BADD49B61CCA68BA0093257E /* QProtoParser.h in Headers */ = {isa = PBXBuildFile; fileRef = BADD49B41CCA68BA0093257E /* QProtoParser.h */; }; BAFC847219C44AFD00C18B22 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840219C44AFD00C18B22 /* Buffer.h */; }; BAFC847319C44AFD00C18B22 /* ClientSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840419C44AFD00C18B22 /* ClientSocket.cc */; }; BAFC847419C44AFD00C18B22 /* ClientSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840519C44AFD00C18B22 /* ClientSocket.h */; }; BAFC847519C44AFD00C18B22 /* ConfigParser.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840719C44AFD00C18B22 /* ConfigParser.cc */; }; BAFC847619C44AFD00C18B22 /* ConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840819C44AFD00C18B22 /* ConfigParser.h */; }; BAFC847719C44AFD00C18B22 /* EPoller.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840919C44AFD00C18B22 /* EPoller.cc */; }; BAFC847819C44AFD00C18B22 /* EPoller.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840A19C44AFD00C18B22 /* EPoller.h */; }; BAFC847919C44AFD00C18B22 /* Kqueue.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840B19C44AFD00C18B22 /* Kqueue.cc */; }; BAFC847A19C44AFD00C18B22 /* Kqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840C19C44AFD00C18B22 /* Kqueue.h */; }; BAFC847B19C44AFD00C18B22 /* ListenSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC840D19C44AFD00C18B22 /* ListenSocket.cc */; }; BAFC847C19C44AFD00C18B22 /* ListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC840E19C44AFD00C18B22 /* ListenSocket.h */; }; BAFC847D19C44AFD00C18B22 /* Logger.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC841019C44AFD00C18B22 /* Logger.cc */; }; BAFC847E19C44AFD00C18B22 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC841119C44AFD00C18B22 /* Logger.h */; }; BAFC848E19C44AFD00C18B22 /* NetThreadPool.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */; }; BAFC848F19C44AFD00C18B22 /* NetThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842B19C44AFD00C18B22 /* NetThreadPool.h */; }; BAFC849019C44AFD00C18B22 /* Poller.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842C19C44AFD00C18B22 /* Poller.h */; }; BAFC849119C44AFD00C18B22 /* Server.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC842D19C44AFD00C18B22 /* Server.cc */; }; BAFC849219C44AFD00C18B22 /* Server.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC842E19C44AFD00C18B22 /* Server.h */; }; BAFC84B719C44AFD00C18B22 /* Socket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846019C44AFD00C18B22 /* Socket.cc */; }; BAFC84B819C44AFD00C18B22 /* Socket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846119C44AFD00C18B22 /* Socket.h */; }; BAFC84B919C44AFD00C18B22 /* StreamSocket.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846219C44AFD00C18B22 /* StreamSocket.cc */; }; BAFC84BA19C44AFD00C18B22 /* StreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846319C44AFD00C18B22 /* StreamSocket.h */; }; BAFC84BB19C44AFD00C18B22 /* TaskManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846419C44AFD00C18B22 /* TaskManager.cc */; }; BAFC84BC19C44AFD00C18B22 /* TaskManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846519C44AFD00C18B22 /* TaskManager.h */; }; BAFC84C219C44AFD00C18B22 /* ThreadPool.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846C19C44AFD00C18B22 /* ThreadPool.cc */; }; BAFC84C319C44AFD00C18B22 /* ThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846D19C44AFD00C18B22 /* ThreadPool.h */; }; BAFC84C419C44AFD00C18B22 /* Timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC846E19C44AFD00C18B22 /* Timer.cc */; }; BAFC84C519C44AFD00C18B22 /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC846F19C44AFD00C18B22 /* Timer.h */; }; BAFC84C619C44AFD00C18B22 /* UnboundedBuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */; }; BAFC84C719C44AFD00C18B22 /* UnboundedBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */; }; BAFFCB531A8F09CA00C47F92 /* QDB.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB511A8F09CA00C47F92 /* QDB.cc */; }; BAFFCB541A8F09CA00C47F92 /* QDB.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB521A8F09CA00C47F92 /* QDB.h */; }; BAFFCB5A1A9244B200C47F92 /* lzf_c.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB561A9244B200C47F92 /* lzf_c.c */; }; BAFFCB5B1A9244B200C47F92 /* lzf_d.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB571A9244B200C47F92 /* lzf_d.c */; }; BAFFCB5C1A9244B200C47F92 /* lzf.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB581A9244B200C47F92 /* lzf.h */; }; BAFFCB5D1A9244B200C47F92 /* lzfP.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB591A9244B200C47F92 /* lzfP.h */; }; BAFFCB5F1A9423DC00C47F92 /* crc64.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB5E1A9423DC00C47F92 /* crc64.c */; }; BAFFCB621A9EA0AC00C47F92 /* redisZipList.c in Sources */ = {isa = PBXBuildFile; fileRef = BAFFCB601A9EA0AC00C47F92 /* redisZipList.c */; }; BAFFCB631A9EA0AC00C47F92 /* redisZipList.h in Headers */ = {isa = PBXBuildFile; fileRef = BAFFCB611A9EA0AC00C47F92 /* redisZipList.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ BA34F8DB1D5C190900F1DE01 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BA34F6021D5C151700F1DE01; remoteInfo = leveldb; }; BA34F8DD1D5C191300F1DE01 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BA34F6021D5C151700F1DE01; remoteInfo = leveldb; }; BA4CD22E1CEB55B400F68F67 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAD0C5D819E79FF7005B3784; remoteInfo = qedislib; }; BA6E47A91F99066700EFBE6F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAFC83FD19C44AE800C18B22; remoteInfo = qbaselib; }; BA6E47AB1F99066700EFBE6F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAD0C5D819E79FF7005B3784; remoteInfo = qedislib; }; BAD0C5DD19E7A002005B3784 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAFC83FD19C44AE800C18B22; remoteInfo = qbaselib; }; BAD0C5DF19E7A007005B3784 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAD0C5D819E79FF7005B3784; remoteInfo = qedislib; }; BAD0C62019E7A889005B3784 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAFC83FD19C44AE800C18B22; remoteInfo = qbaselib; }; BAD0C62219E7A88D005B3784 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAD0C5D819E79FF7005B3784; remoteInfo = qedislib; }; BAFC84D419C44B6800C18B22 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BABEBECD19C4474900010636 /* Project object */; proxyType = 1; remoteGlobalIDString = BAFC83FD19C44AE800C18B22; remoteInfo = qbaselib; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ BABEBED319C4474900010636 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; BADC7D6F19D6BC700036858E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ BA12506F1A6D43D500A0532D /* MemoryFile.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryFile.cc; sourceTree = ""; }; BA1250701A6D43D500A0532D /* MemoryFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryFile.h; sourceTree = ""; }; BA12507F1A7338CD00A0532D /* AsyncBuffer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncBuffer.cc; path = QBase/AsyncBuffer.cc; sourceTree = ""; }; BA1250801A7338CD00A0532D /* AsyncBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncBuffer.h; path = QBase/AsyncBuffer.h; sourceTree = ""; }; BA29C44C19FB2CEE00796E3A /* QMulti.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QMulti.cc; path = QedisCore/QMulti.cc; sourceTree = SOURCE_ROOT; }; BA29C44D19FB2CEE00796E3A /* QMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QMulti.h; path = QedisCore/QMulti.h; sourceTree = SOURCE_ROOT; }; BA2C0C7A1CDC1DC800BDF050 /* Delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Delegate.h; path = QBase/Delegate.h; sourceTree = ""; }; BA34F6031D5C151700F1DE01 /* libleveldb.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libleveldb.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; BA34F8E31D5C199D00F1DE01 /* builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builder.cc; sourceTree = ""; }; BA34F8E41D5C199D00F1DE01 /* builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builder.h; sourceTree = ""; }; BA34F8E51D5C199D00F1DE01 /* c.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = c.cc; sourceTree = ""; }; BA34F8E81D5C199D00F1DE01 /* db_bench.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_bench.cc; sourceTree = ""; }; BA34F8E91D5C199D00F1DE01 /* db_impl.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_impl.cc; sourceTree = ""; }; BA34F8EA1D5C199D00F1DE01 /* db_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_impl.h; sourceTree = ""; }; BA34F8EB1D5C199D00F1DE01 /* db_iter.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = db_iter.cc; sourceTree = ""; }; BA34F8EC1D5C199D00F1DE01 /* db_iter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_iter.h; sourceTree = ""; }; BA34F8EE1D5C199D00F1DE01 /* dbformat.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dbformat.cc; sourceTree = ""; }; BA34F8EF1D5C199D00F1DE01 /* dbformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dbformat.h; sourceTree = ""; }; BA34F8F11D5C199D00F1DE01 /* dumpfile.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dumpfile.cc; sourceTree = ""; }; BA34F8F31D5C199D00F1DE01 /* filename.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filename.cc; sourceTree = ""; }; BA34F8F41D5C199D00F1DE01 /* filename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filename.h; sourceTree = ""; }; BA34F8F71D5C199D00F1DE01 /* log_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_format.h; sourceTree = ""; }; BA34F8F81D5C199D00F1DE01 /* log_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_reader.cc; sourceTree = ""; }; BA34F8F91D5C199D00F1DE01 /* log_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_reader.h; sourceTree = ""; }; BA34F8FB1D5C199D00F1DE01 /* log_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_writer.cc; sourceTree = ""; }; BA34F8FC1D5C199D00F1DE01 /* log_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log_writer.h; sourceTree = ""; }; BA34F8FD1D5C199D00F1DE01 /* memtable.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memtable.cc; sourceTree = ""; }; BA34F8FE1D5C199D00F1DE01 /* memtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memtable.h; sourceTree = ""; }; BA34F8FF1D5C199D00F1DE01 /* repair.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = repair.cc; sourceTree = ""; }; BA34F9001D5C199D00F1DE01 /* skiplist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skiplist.h; sourceTree = ""; }; BA34F9021D5C199D00F1DE01 /* snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = snapshot.h; sourceTree = ""; }; BA34F9031D5C199D00F1DE01 /* table_cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table_cache.cc; sourceTree = ""; }; BA34F9041D5C199D00F1DE01 /* table_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table_cache.h; sourceTree = ""; }; BA34F9051D5C199D00F1DE01 /* version_edit.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version_edit.cc; sourceTree = ""; }; BA34F9061D5C199D00F1DE01 /* version_edit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version_edit.h; sourceTree = ""; }; BA34F9081D5C199D00F1DE01 /* version_set.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version_set.cc; sourceTree = ""; }; BA34F9091D5C199D00F1DE01 /* version_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version_set.h; sourceTree = ""; }; BA34F90B1D5C199D00F1DE01 /* write_batch.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write_batch.cc; sourceTree = ""; }; BA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write_batch_internal.h; sourceTree = ""; }; BA34F91A1D5C199D00F1DE01 /* memenv.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memenv.cc; sourceTree = ""; }; BA34F91B1D5C199D00F1DE01 /* memenv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memenv.h; sourceTree = ""; }; BA34F91F1D5C199D00F1DE01 /* c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c.h; sourceTree = ""; }; BA34F9201D5C199D00F1DE01 /* cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cache.h; sourceTree = ""; }; BA34F9211D5C199D00F1DE01 /* comparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = comparator.h; sourceTree = ""; }; BA34F9221D5C199D00F1DE01 /* db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db.h; sourceTree = ""; }; BA34F9231D5C199D00F1DE01 /* dumpfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dumpfile.h; sourceTree = ""; }; BA34F9241D5C199D00F1DE01 /* env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = env.h; sourceTree = ""; }; BA34F9251D5C199D00F1DE01 /* filter_policy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter_policy.h; sourceTree = ""; }; BA34F9261D5C199D00F1DE01 /* iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = ""; }; BA34F9271D5C199D00F1DE01 /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = ""; }; BA34F9281D5C199D00F1DE01 /* slice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = slice.h; sourceTree = ""; }; BA34F9291D5C199D00F1DE01 /* status.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = status.h; sourceTree = ""; }; BA34F92A1D5C199D00F1DE01 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; BA34F92B1D5C199D00F1DE01 /* table_builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table_builder.h; sourceTree = ""; }; BA34F92C1D5C199D00F1DE01 /* write_batch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write_batch.h; sourceTree = ""; }; BA34F9321D5C199D00F1DE01 /* atomic_pointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomic_pointer.h; sourceTree = ""; }; BA34F9331D5C199D00F1DE01 /* port.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port.h; sourceTree = ""; }; BA34F9341D5C199D00F1DE01 /* port_example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port_example.h; sourceTree = ""; }; BA34F9351D5C199D00F1DE01 /* port_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = port_posix.cc; sourceTree = ""; }; BA34F9361D5C199D00F1DE01 /* port_posix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port_posix.h; sourceTree = ""; }; BA34F9381D5C199D00F1DE01 /* thread_annotations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_annotations.h; sourceTree = ""; }; BA34F93D1D5C199D00F1DE01 /* block.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = block.cc; sourceTree = ""; }; BA34F93E1D5C199D00F1DE01 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block.h; sourceTree = ""; }; BA34F93F1D5C199D00F1DE01 /* block_builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = block_builder.cc; sourceTree = ""; }; BA34F9401D5C199D00F1DE01 /* block_builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block_builder.h; sourceTree = ""; }; BA34F9411D5C199D00F1DE01 /* filter_block.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_block.cc; sourceTree = ""; }; BA34F9421D5C199D00F1DE01 /* filter_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter_block.h; sourceTree = ""; }; BA34F9441D5C199D00F1DE01 /* format.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cc; sourceTree = ""; }; BA34F9451D5C199D00F1DE01 /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = format.h; sourceTree = ""; }; BA34F9461D5C199D00F1DE01 /* iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iterator.cc; sourceTree = ""; }; BA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterator_wrapper.h; sourceTree = ""; }; BA34F9481D5C199D00F1DE01 /* merger.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = merger.cc; sourceTree = ""; }; BA34F9491D5C199D00F1DE01 /* merger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = merger.h; sourceTree = ""; }; BA34F94A1D5C199D00F1DE01 /* table.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cc; sourceTree = ""; }; BA34F94B1D5C199D00F1DE01 /* table_builder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table_builder.cc; sourceTree = ""; }; BA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = two_level_iterator.cc; sourceTree = ""; }; BA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = two_level_iterator.h; sourceTree = ""; }; BA34F9511D5C199D00F1DE01 /* arena.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = arena.cc; sourceTree = ""; }; BA34F9521D5C199D00F1DE01 /* arena.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arena.h; sourceTree = ""; }; BA34F9541D5C199D00F1DE01 /* bloom.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bloom.cc; sourceTree = ""; }; BA34F9561D5C199D00F1DE01 /* cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cache.cc; sourceTree = ""; }; BA34F9581D5C199D00F1DE01 /* coding.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coding.cc; sourceTree = ""; }; BA34F9591D5C199D00F1DE01 /* coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding.h; sourceTree = ""; }; BA34F95B1D5C199D00F1DE01 /* comparator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = comparator.cc; sourceTree = ""; }; BA34F95C1D5C199D00F1DE01 /* crc32c.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = crc32c.cc; sourceTree = ""; }; BA34F95D1D5C199D00F1DE01 /* crc32c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc32c.h; sourceTree = ""; }; BA34F95F1D5C199D00F1DE01 /* env.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env.cc; sourceTree = ""; }; BA34F9601D5C199D00F1DE01 /* env_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env_posix.cc; sourceTree = ""; }; BA34F9621D5C199D00F1DE01 /* filter_policy.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_policy.cc; sourceTree = ""; }; BA34F9631D5C199D00F1DE01 /* hash.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hash.cc; sourceTree = ""; }; BA34F9641D5C199D00F1DE01 /* hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hash.h; sourceTree = ""; }; BA34F9661D5C199D00F1DE01 /* histogram.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = histogram.cc; sourceTree = ""; }; BA34F9671D5C199D00F1DE01 /* histogram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = histogram.h; sourceTree = ""; }; BA34F9681D5C199D00F1DE01 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cc; sourceTree = ""; }; BA34F9691D5C199D00F1DE01 /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = ""; }; BA34F96A1D5C199D00F1DE01 /* mutexlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mutexlock.h; sourceTree = ""; }; BA34F96B1D5C199D00F1DE01 /* options.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = options.cc; sourceTree = ""; }; BA34F96C1D5C199D00F1DE01 /* posix_logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = posix_logger.h; sourceTree = ""; }; BA34F96D1D5C199D00F1DE01 /* random.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = random.h; sourceTree = ""; }; BA34F96E1D5C199D00F1DE01 /* status.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status.cc; sourceTree = ""; }; BA34F96F1D5C199D00F1DE01 /* testharness.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testharness.cc; sourceTree = ""; }; BA34F9701D5C199D00F1DE01 /* testharness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testharness.h; sourceTree = ""; }; BA34F9711D5C199D00F1DE01 /* testutil.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testutil.cc; sourceTree = ""; }; BA34F9721D5C199D00F1DE01 /* testutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testutil.h; sourceTree = ""; }; BA34F9F11D5C1C9700F1DE01 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; BA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QDumpInterface.h; path = QedisCore/QDumpInterface.h; sourceTree = SOURCE_ROOT; }; BA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QLeveldb.cc; path = QedisCore/QLeveldb.cc; sourceTree = SOURCE_ROOT; }; BA34F9F71D5C25E400F1DE01 /* QLeveldb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QLeveldb.h; path = QedisCore/QLeveldb.h; sourceTree = SOURCE_ROOT; }; BA34F9FD1D5C269D00F1DE01 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; BA3B0AD41ACBA9E1000D9399 /* QAOF.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QAOF.cc; path = QedisCore/QAOF.cc; sourceTree = SOURCE_ROOT; }; BA3B0AD51ACBA9E1000D9399 /* QAOF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QAOF.h; path = QedisCore/QAOF.h; sourceTree = SOURCE_ROOT; }; BA4053911BA1AE3B00B93053 /* QReplication.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QReplication.cc; path = QedisCore/QReplication.cc; sourceTree = SOURCE_ROOT; }; BA4053921BA1AE3B00B93053 /* QReplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QReplication.h; path = QedisCore/QReplication.h; sourceTree = SOURCE_ROOT; }; BA47763B1AB0773F00EDBB5D /* redisIntset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = redisIntset.c; path = QedisCore/redisIntset.c; sourceTree = SOURCE_ROOT; }; BA47763C1AB0773F00EDBB5D /* redisIntset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = redisIntset.h; path = QedisCore/redisIntset.h; sourceTree = SOURCE_ROOT; }; BA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libqedismodule.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; BA4CD21D1CEB54FF00F68F67 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = Modules/CMakeLists.txt; sourceTree = ""; }; BA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHashModule.cc; path = Modules/QHashModule.cc; sourceTree = ""; }; BA4CD21F1CEB54FF00F68F67 /* QHashModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHashModule.h; path = Modules/QHashModule.h; sourceTree = ""; }; BA4CD2201CEB54FF00F68F67 /* QListModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QListModule.cc; path = Modules/QListModule.cc; sourceTree = ""; }; BA4CD2211CEB54FF00F68F67 /* QListModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QListModule.h; path = Modules/QListModule.h; sourceTree = ""; }; BA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QModuleInit.cc; path = Modules/QModuleInit.cc; sourceTree = ""; }; BA4CD2231CEB54FF00F68F67 /* QModuleInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QModuleInit.h; path = Modules/QModuleInit.h; sourceTree = ""; }; BA4CD2241CEB54FF00F68F67 /* QSetModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSetModule.cc; path = Modules/QSetModule.cc; sourceTree = ""; }; BA4CD2251CEB54FF00F68F67 /* QSetModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSetModule.h; path = Modules/QSetModule.h; sourceTree = ""; }; BA4CD2301CEB61E100F68F67 /* QModule.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QModule.cc; path = QedisCore/QModule.cc; sourceTree = SOURCE_ROOT; }; BA4CD2311CEB61E100F68F67 /* QModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QModule.h; path = QedisCore/QModule.h; sourceTree = SOURCE_ROOT; }; BA4CD2361CEBF10600F68F67 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = CMakeLists.txt; path = QedisCore/CMakeLists.txt; sourceTree = SOURCE_ROOT; }; BA6C336A19EA77B200AC1800 /* QGlobRegex.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QGlobRegex.cc; path = QedisCore/QGlobRegex.cc; sourceTree = SOURCE_ROOT; }; BA6C336B19EA77B200AC1800 /* QGlobRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QGlobRegex.h; path = QedisCore/QGlobRegex.h; sourceTree = SOURCE_ROOT; }; BA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QGlobRegex_unittest.cc; sourceTree = ""; }; BA6C337019EC0D9900AC1800 /* 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; }; BA6E478C1F99056000EFBE6F /* QSlaveClient.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSlaveClient.cc; path = QedisCore/QSlaveClient.cc; sourceTree = SOURCE_ROOT; }; BA6E478D1F99056000EFBE6F /* QSlaveClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSlaveClient.h; path = QedisCore/QSlaveClient.h; sourceTree = SOURCE_ROOT; }; BA6E47911F9905E600EFBE6F /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QSentinel/CMakeLists.txt; sourceTree = ""; }; BA6E47921F9905E600EFBE6F /* QClusterClient.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QClusterClient.cc; path = QSentinel/QClusterClient.cc; sourceTree = ""; }; BA6E47931F9905E600EFBE6F /* QClusterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClusterClient.h; path = QSentinel/QClusterClient.h; sourceTree = ""; }; BA6E47941F9905E600EFBE6F /* QClusterInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClusterInterface.h; path = QSentinel/QClusterInterface.h; sourceTree = ""; }; BA6E47961F9905F500EFBE6F /* proto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proto.h; path = QSentinel/zookeeper/proto.h; sourceTree = ""; }; BA6E47971F9905F500EFBE6F /* recordio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = recordio.c; path = QSentinel/zookeeper/recordio.c; sourceTree = ""; }; BA6E47981F9905F500EFBE6F /* recordio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = recordio.h; path = QSentinel/zookeeper/recordio.h; sourceTree = ""; }; BA6E47991F9905F500EFBE6F /* zookeeper_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zookeeper_version.h; path = QSentinel/zookeeper/zookeeper_version.h; sourceTree = ""; }; BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zookeeper.jute.c; path = QSentinel/zookeeper/zookeeper.jute.c; sourceTree = ""; }; BA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zookeeper.jute.h; path = QSentinel/zookeeper/zookeeper.jute.h; sourceTree = ""; }; BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZookeeperConn.cc; path = QSentinel/zookeeper/ZookeeperConn.cc; sourceTree = ""; }; BA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZookeeperConn.h; path = QSentinel/zookeeper/ZookeeperConn.h; sourceTree = ""; }; BA6E47A51F99064D00EFBE6F /* libqcluster.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libqcluster.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; BA98F83B1B01B16600ABCBD9 /* QConfig.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QConfig.cc; path = QedisCore/QConfig.cc; sourceTree = SOURCE_ROOT; }; BA98F83C1B01B16600ABCBD9 /* QConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QConfig.h; path = QedisCore/QConfig.h; sourceTree = SOURCE_ROOT; }; BA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSlowLog.cc; path = QedisCore/QSlowLog.cc; sourceTree = SOURCE_ROOT; }; BA98F8401B1B539600ABCBD9 /* QSlowLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSlowLog.h; path = QedisCore/QSlowLog.h; sourceTree = SOURCE_ROOT; }; BA99A7591CF19ED20051C43C /* qedis.conf */ = {isa = PBXFileReference; lastKnownFileType = text; path = qedis.conf; sourceTree = ""; }; BA99CC521C52852C00BB88F7 /* Qedis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Qedis.h; path = QedisSvr/Qedis.h; sourceTree = ""; }; BAB2265C2007B7B7005241F6 /* QMigration.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QMigration.cc; path = QedisCore/QMigration.cc; sourceTree = SOURCE_ROOT; }; BAB2265D2007B7B7005241F6 /* QMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QMigration.h; path = QedisCore/QMigration.h; sourceTree = SOURCE_ROOT; }; BABEBED519C4474900010636 /* Qedis */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Qedis; sourceTree = BUILT_PRODUCTS_DIR; }; BAC9098D1B9D30DB002003C2 /* CMakeCommon */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeCommon; sourceTree = ""; }; BAC9098E1B9D30DB002003C2 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libqedislib.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; BAD0C5E319E7A02C005B3784 /* 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; }; BAD0C5E419E7A02C005B3784 /* QClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QClient.h; path = QedisCore/QClient.h; sourceTree = SOURCE_ROOT; }; BAD0C5E519E7A02C005B3784 /* 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; }; BAD0C5E619E7A02C005B3784 /* 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; }; BAD0C5E719E7A02C005B3784 /* QCommon.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QCommon.cc; path = QedisCore/QCommon.cc; sourceTree = SOURCE_ROOT; }; BAD0C5E819E7A02C005B3784 /* 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; }; BAD0C5EA19E7A02C005B3784 /* QHash.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHash.cc; path = QedisCore/QHash.cc; sourceTree = SOURCE_ROOT; }; BAD0C5EB19E7A02C005B3784 /* QHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHash.h; path = QedisCore/QHash.h; sourceTree = SOURCE_ROOT; }; BAD0C5EC19E7A02C005B3784 /* 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; }; BAD0C5ED19E7A02C005B3784 /* QList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QList.cc; path = QedisCore/QList.cc; sourceTree = SOURCE_ROOT; }; BAD0C5EE19E7A02C005B3784 /* QList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QList.h; path = QedisCore/QList.h; sourceTree = SOURCE_ROOT; }; BAD0C5EF19E7A02C005B3784 /* QPubsub.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QPubsub.cc; path = QedisCore/QPubsub.cc; sourceTree = SOURCE_ROOT; }; BAD0C5F019E7A02C005B3784 /* QServerCommand.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QServerCommand.cc; path = QedisCore/QServerCommand.cc; sourceTree = SOURCE_ROOT; }; BAD0C5F119E7A02C005B3784 /* QSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSet.cc; path = QedisCore/QSet.cc; sourceTree = SOURCE_ROOT; }; BAD0C5F219E7A02C005B3784 /* QSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSet.h; path = QedisCore/QSet.h; sourceTree = SOURCE_ROOT; }; BAD0C5F319E7A02C005B3784 /* QSortedSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QSortedSet.cc; path = QedisCore/QSortedSet.cc; sourceTree = SOURCE_ROOT; }; BAD0C5F419E7A02C005B3784 /* QSortedSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSortedSet.h; path = QedisCore/QSortedSet.h; sourceTree = SOURCE_ROOT; }; BAD0C5F719E7A02C005B3784 /* 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; }; BAD0C5F819E7A02C005B3784 /* 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; }; BAD0C5F919E7A02C005B3784 /* 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; }; BAD0C5FA19E7A02C005B3784 /* 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; }; BAD0C5FB19E7A02C005B3784 /* QHelper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QHelper.cc; path = QedisCore/QHelper.cc; sourceTree = SOURCE_ROOT; }; BAD0C5FC19E7A02C005B3784 /* QHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QHelper.h; path = QedisCore/QHelper.h; sourceTree = SOURCE_ROOT; }; BAD0C61919E7A075005B3784 /* Qedis.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Qedis.cc; path = QedisSvr/Qedis.cc; sourceTree = ""; }; BAD0C61C19E7A09F005B3784 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QedisSvr/CMakeLists.txt; sourceTree = ""; }; BAD0C61D19E7A18B005B3784 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; BAD0C62919E7A9D8005B3784 /* UnitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = UnitTest.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BADC7D7119D6BC700036858E /* UnitTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = UnitTest; sourceTree = BUILT_PRODUCTS_DIR; }; BADC7D7A19D6BC960036858E /* UnitTest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = UnitTest.cc; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; BADD49B31CCA68BA0093257E /* QProtoParser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QProtoParser.cc; path = QedisCore/QProtoParser.cc; sourceTree = SOURCE_ROOT; }; BADD49B41CCA68BA0093257E /* QProtoParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QProtoParser.h; path = QedisCore/QProtoParser.h; sourceTree = SOURCE_ROOT; }; BAFBDDB51CECAB2400E12FBA /* QedisLogo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QedisLogo.h; path = QedisSvr/QedisLogo.h; sourceTree = ""; }; BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libqbaselib.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; BAFC840219C44AFD00C18B22 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Buffer.h; path = QBase/Buffer.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BAFC840419C44AFD00C18B22 /* ClientSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClientSocket.cc; path = QBase/ClientSocket.cc; sourceTree = ""; }; BAFC840519C44AFD00C18B22 /* ClientSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClientSocket.h; path = QBase/ClientSocket.h; sourceTree = ""; }; BAFC840619C44AFD00C18B22 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = QBase/CMakeLists.txt; sourceTree = ""; }; BAFC840719C44AFD00C18B22 /* ConfigParser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = ConfigParser.cc; path = QBase/ConfigParser.cc; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; BAFC840819C44AFD00C18B22 /* ConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = ConfigParser.h; path = QBase/ConfigParser.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BAFC840919C44AFD00C18B22 /* EPoller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = EPoller.cc; path = QBase/EPoller.cc; sourceTree = ""; }; BAFC840A19C44AFD00C18B22 /* EPoller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EPoller.h; path = QBase/EPoller.h; sourceTree = ""; }; BAFC840B19C44AFD00C18B22 /* Kqueue.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Kqueue.cc; path = QBase/Kqueue.cc; sourceTree = ""; }; BAFC840C19C44AFD00C18B22 /* Kqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Kqueue.h; path = QBase/Kqueue.h; sourceTree = ""; }; BAFC840D19C44AFD00C18B22 /* ListenSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ListenSocket.cc; path = QBase/ListenSocket.cc; sourceTree = ""; }; BAFC840E19C44AFD00C18B22 /* ListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ListenSocket.h; path = QBase/ListenSocket.h; sourceTree = ""; }; BAFC841019C44AFD00C18B22 /* Logger.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Logger.cc; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; BAFC841119C44AFD00C18B22 /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Logger.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetThreadPool.cc; path = QBase/NetThreadPool.cc; sourceTree = ""; }; BAFC842B19C44AFD00C18B22 /* NetThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetThreadPool.h; path = QBase/NetThreadPool.h; sourceTree = ""; }; BAFC842C19C44AFD00C18B22 /* Poller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Poller.h; path = QBase/Poller.h; sourceTree = ""; }; BAFC842D19C44AFD00C18B22 /* Server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Server.cc; path = QBase/Server.cc; sourceTree = ""; }; BAFC842E19C44AFD00C18B22 /* Server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Server.h; path = QBase/Server.h; sourceTree = ""; }; BAFC846019C44AFD00C18B22 /* Socket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = Socket.cc; path = QBase/Socket.cc; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; BAFC846119C44AFD00C18B22 /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Socket.h; path = QBase/Socket.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BAFC846219C44AFD00C18B22 /* StreamSocket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamSocket.cc; path = QBase/StreamSocket.cc; sourceTree = ""; }; BAFC846319C44AFD00C18B22 /* StreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamSocket.h; path = QBase/StreamSocket.h; sourceTree = ""; }; BAFC846419C44AFD00C18B22 /* TaskManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TaskManager.cc; path = QBase/TaskManager.cc; sourceTree = ""; }; BAFC846519C44AFD00C18B22 /* TaskManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TaskManager.h; path = QBase/TaskManager.h; sourceTree = ""; }; BAFC846C19C44AFD00C18B22 /* ThreadPool.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadPool.cc; sourceTree = ""; }; BAFC846D19C44AFD00C18B22 /* ThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ThreadPool.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; BAFC846E19C44AFD00C18B22 /* Timer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Timer.cc; path = QBase/Timer.cc; sourceTree = ""; }; BAFC846F19C44AFD00C18B22 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Timer.h; path = QBase/Timer.h; sourceTree = ""; }; BAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnboundedBuffer.cc; path = QBase/UnboundedBuffer.cc; sourceTree = ""; }; BAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnboundedBuffer.h; path = QBase/UnboundedBuffer.h; sourceTree = ""; }; BAFFCB511A8F09CA00C47F92 /* QDB.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = QDB.cc; path = QedisCore/QDB.cc; sourceTree = SOURCE_ROOT; }; BAFFCB521A8F09CA00C47F92 /* QDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QDB.h; path = QedisCore/QDB.h; sourceTree = SOURCE_ROOT; }; BAFFCB561A9244B200C47F92 /* lzf_c.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzf_c.c; path = QBase/lzf/lzf_c.c; sourceTree = ""; }; BAFFCB571A9244B200C47F92 /* lzf_d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzf_d.c; path = QBase/lzf/lzf_d.c; sourceTree = ""; }; BAFFCB581A9244B200C47F92 /* lzf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lzf.h; path = QBase/lzf/lzf.h; sourceTree = ""; }; BAFFCB591A9244B200C47F92 /* lzfP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lzfP.h; path = QBase/lzf/lzfP.h; sourceTree = ""; }; BAFFCB5E1A9423DC00C47F92 /* crc64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = crc64.c; path = QedisCore/crc64.c; sourceTree = SOURCE_ROOT; }; BAFFCB601A9EA0AC00C47F92 /* redisZipList.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = redisZipList.c; path = QedisCore/redisZipList.c; sourceTree = SOURCE_ROOT; }; BAFFCB611A9EA0AC00C47F92 /* redisZipList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = redisZipList.h; path = QedisCore/redisZipList.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ BA34F6001D5C151700F1DE01 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; BA4CD2161CEB54F000F68F67 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; BA6E47A21F99064D00EFBE6F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; BABEBED219C4474900010636 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BA34F9FC1D5C266200F1DE01 /* libleveldb.dylib in Frameworks */, BA34F9FB1D5C265D00F1DE01 /* libqbaselib.dylib in Frameworks */, BA34F9F31D5C243800F1DE01 /* libqedislib.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; BAD0C5D619E79FF7005B3784 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BA34F9F41D5C244E00F1DE01 /* libqbaselib.dylib in Frameworks */, BA34F9F21D5C221700F1DE01 /* libleveldb.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; BADC7D6E19D6BC700036858E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BAD0C62519E7A8DA005B3784 /* libqedislib.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; BAFC83FB19C44AE800C18B22 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ BA34F8DF1D5C198B00F1DE01 /* leveldb */ = { isa = PBXGroup; children = ( BA34F8E11D5C199D00F1DE01 /* db */, BA34F9181D5C199D00F1DE01 /* helpers */, BA34F91D1D5C199D00F1DE01 /* include */, BA34F9311D5C199D00F1DE01 /* port */, BA34F93C1D5C199D00F1DE01 /* table */, BA34F9501D5C199D00F1DE01 /* util */, ); name = leveldb; sourceTree = ""; }; BA34F8E11D5C199D00F1DE01 /* db */ = { isa = PBXGroup; children = ( BA34F8E31D5C199D00F1DE01 /* builder.cc */, BA34F8E41D5C199D00F1DE01 /* builder.h */, BA34F8E51D5C199D00F1DE01 /* c.cc */, BA34F8E81D5C199D00F1DE01 /* db_bench.cc */, BA34F8E91D5C199D00F1DE01 /* db_impl.cc */, BA34F8EA1D5C199D00F1DE01 /* db_impl.h */, BA34F8EB1D5C199D00F1DE01 /* db_iter.cc */, BA34F8EC1D5C199D00F1DE01 /* db_iter.h */, BA34F8EE1D5C199D00F1DE01 /* dbformat.cc */, BA34F8EF1D5C199D00F1DE01 /* dbformat.h */, BA34F8F11D5C199D00F1DE01 /* dumpfile.cc */, BA34F8F31D5C199D00F1DE01 /* filename.cc */, BA34F8F41D5C199D00F1DE01 /* filename.h */, BA34F8F71D5C199D00F1DE01 /* log_format.h */, BA34F8F81D5C199D00F1DE01 /* log_reader.cc */, BA34F8F91D5C199D00F1DE01 /* log_reader.h */, BA34F8FB1D5C199D00F1DE01 /* log_writer.cc */, BA34F8FC1D5C199D00F1DE01 /* log_writer.h */, BA34F8FD1D5C199D00F1DE01 /* memtable.cc */, BA34F8FE1D5C199D00F1DE01 /* memtable.h */, BA34F8FF1D5C199D00F1DE01 /* repair.cc */, BA34F9001D5C199D00F1DE01 /* skiplist.h */, BA34F9021D5C199D00F1DE01 /* snapshot.h */, BA34F9031D5C199D00F1DE01 /* table_cache.cc */, BA34F9041D5C199D00F1DE01 /* table_cache.h */, BA34F9051D5C199D00F1DE01 /* version_edit.cc */, BA34F9061D5C199D00F1DE01 /* version_edit.h */, BA34F9081D5C199D00F1DE01 /* version_set.cc */, BA34F9091D5C199D00F1DE01 /* version_set.h */, BA34F90B1D5C199D00F1DE01 /* write_batch.cc */, BA34F90C1D5C199D00F1DE01 /* write_batch_internal.h */, ); name = db; path = leveldb/db; sourceTree = ""; }; BA34F9181D5C199D00F1DE01 /* helpers */ = { isa = PBXGroup; children = ( BA34F9191D5C199D00F1DE01 /* memenv */, ); name = helpers; path = leveldb/helpers; sourceTree = ""; }; BA34F9191D5C199D00F1DE01 /* memenv */ = { isa = PBXGroup; children = ( BA34F91A1D5C199D00F1DE01 /* memenv.cc */, BA34F91B1D5C199D00F1DE01 /* memenv.h */, ); path = memenv; sourceTree = ""; }; BA34F91D1D5C199D00F1DE01 /* include */ = { isa = PBXGroup; children = ( BA34F91E1D5C199D00F1DE01 /* leveldb */, ); name = include; path = leveldb/include; sourceTree = ""; }; BA34F91E1D5C199D00F1DE01 /* leveldb */ = { isa = PBXGroup; children = ( BA34F91F1D5C199D00F1DE01 /* c.h */, BA34F9201D5C199D00F1DE01 /* cache.h */, BA34F9211D5C199D00F1DE01 /* comparator.h */, BA34F9221D5C199D00F1DE01 /* db.h */, BA34F9231D5C199D00F1DE01 /* dumpfile.h */, BA34F9241D5C199D00F1DE01 /* env.h */, BA34F9251D5C199D00F1DE01 /* filter_policy.h */, BA34F9261D5C199D00F1DE01 /* iterator.h */, BA34F9271D5C199D00F1DE01 /* options.h */, BA34F9281D5C199D00F1DE01 /* slice.h */, BA34F9291D5C199D00F1DE01 /* status.h */, BA34F92A1D5C199D00F1DE01 /* table.h */, BA34F92B1D5C199D00F1DE01 /* table_builder.h */, BA34F92C1D5C199D00F1DE01 /* write_batch.h */, ); path = leveldb; sourceTree = ""; }; BA34F9311D5C199D00F1DE01 /* port */ = { isa = PBXGroup; children = ( BA34F9321D5C199D00F1DE01 /* atomic_pointer.h */, BA34F9331D5C199D00F1DE01 /* port.h */, BA34F9341D5C199D00F1DE01 /* port_example.h */, BA34F9351D5C199D00F1DE01 /* port_posix.cc */, BA34F9361D5C199D00F1DE01 /* port_posix.h */, BA34F9381D5C199D00F1DE01 /* thread_annotations.h */, ); name = port; path = leveldb/port; sourceTree = ""; }; BA34F93C1D5C199D00F1DE01 /* table */ = { isa = PBXGroup; children = ( BA34F93D1D5C199D00F1DE01 /* block.cc */, BA34F93E1D5C199D00F1DE01 /* block.h */, BA34F93F1D5C199D00F1DE01 /* block_builder.cc */, BA34F9401D5C199D00F1DE01 /* block_builder.h */, BA34F9411D5C199D00F1DE01 /* filter_block.cc */, BA34F9421D5C199D00F1DE01 /* filter_block.h */, BA34F9441D5C199D00F1DE01 /* format.cc */, BA34F9451D5C199D00F1DE01 /* format.h */, BA34F9461D5C199D00F1DE01 /* iterator.cc */, BA34F9471D5C199D00F1DE01 /* iterator_wrapper.h */, BA34F9481D5C199D00F1DE01 /* merger.cc */, BA34F9491D5C199D00F1DE01 /* merger.h */, BA34F94A1D5C199D00F1DE01 /* table.cc */, BA34F94B1D5C199D00F1DE01 /* table_builder.cc */, BA34F94D1D5C199D00F1DE01 /* two_level_iterator.cc */, BA34F94E1D5C199D00F1DE01 /* two_level_iterator.h */, ); name = table; path = leveldb/table; sourceTree = ""; }; BA34F9501D5C199D00F1DE01 /* util */ = { isa = PBXGroup; children = ( BA34F9511D5C199D00F1DE01 /* arena.cc */, BA34F9521D5C199D00F1DE01 /* arena.h */, BA34F9541D5C199D00F1DE01 /* bloom.cc */, BA34F9561D5C199D00F1DE01 /* cache.cc */, BA34F9581D5C199D00F1DE01 /* coding.cc */, BA34F9591D5C199D00F1DE01 /* coding.h */, BA34F95B1D5C199D00F1DE01 /* comparator.cc */, BA34F95C1D5C199D00F1DE01 /* crc32c.cc */, BA34F95D1D5C199D00F1DE01 /* crc32c.h */, BA34F95F1D5C199D00F1DE01 /* env.cc */, BA34F9601D5C199D00F1DE01 /* env_posix.cc */, BA34F9621D5C199D00F1DE01 /* filter_policy.cc */, BA34F9631D5C199D00F1DE01 /* hash.cc */, BA34F9641D5C199D00F1DE01 /* hash.h */, BA34F9661D5C199D00F1DE01 /* histogram.cc */, BA34F9671D5C199D00F1DE01 /* histogram.h */, BA34F9681D5C199D00F1DE01 /* logging.cc */, BA34F9691D5C199D00F1DE01 /* logging.h */, BA34F96A1D5C199D00F1DE01 /* mutexlock.h */, BA34F96B1D5C199D00F1DE01 /* options.cc */, BA34F96C1D5C199D00F1DE01 /* posix_logger.h */, BA34F96D1D5C199D00F1DE01 /* random.h */, BA34F96E1D5C199D00F1DE01 /* status.cc */, BA34F96F1D5C199D00F1DE01 /* testharness.cc */, BA34F9701D5C199D00F1DE01 /* testharness.h */, BA34F9711D5C199D00F1DE01 /* testutil.cc */, BA34F9721D5C199D00F1DE01 /* testutil.h */, ); name = util; path = leveldb/util; sourceTree = ""; }; BA4CD2141CEB54BC00F68F67 /* Modules */ = { isa = PBXGroup; children = ( BA4CD21D1CEB54FF00F68F67 /* CMakeLists.txt */, BA4CD21E1CEB54FF00F68F67 /* QHashModule.cc */, BA4CD21F1CEB54FF00F68F67 /* QHashModule.h */, BA4CD2201CEB54FF00F68F67 /* QListModule.cc */, BA4CD2211CEB54FF00F68F67 /* QListModule.h */, BA4CD2221CEB54FF00F68F67 /* QModuleInit.cc */, BA4CD2231CEB54FF00F68F67 /* QModuleInit.h */, BA4CD2241CEB54FF00F68F67 /* QSetModule.cc */, BA4CD2251CEB54FF00F68F67 /* QSetModule.h */, ); name = Modules; sourceTree = ""; }; BA6E478F1F9905C800EFBE6F /* QSentinel */ = { isa = PBXGroup; children = ( BA6E47911F9905E600EFBE6F /* CMakeLists.txt */, BA6E47921F9905E600EFBE6F /* QClusterClient.cc */, BA6E47931F9905E600EFBE6F /* QClusterClient.h */, BA6E47941F9905E600EFBE6F /* QClusterInterface.h */, BA6E47901F9905D400EFBE6F /* zookeeper */, ); name = QSentinel; sourceTree = ""; }; BA6E47901F9905D400EFBE6F /* zookeeper */ = { isa = PBXGroup; children = ( BA6E47961F9905F500EFBE6F /* proto.h */, BA6E47971F9905F500EFBE6F /* recordio.c */, BA6E47981F9905F500EFBE6F /* recordio.h */, BA6E47991F9905F500EFBE6F /* zookeeper_version.h */, BA6E479A1F9905F500EFBE6F /* zookeeper.jute.c */, BA6E479B1F9905F500EFBE6F /* zookeeper.jute.h */, BA6E479C1F9905F500EFBE6F /* ZookeeperConn.cc */, BA6E479D1F9905F500EFBE6F /* ZookeeperConn.h */, ); name = zookeeper; sourceTree = ""; }; BAA158B219C44A0F007713F9 /* QBase */ = { isa = PBXGroup; children = ( BA2C0C7A1CDC1DC800BDF050 /* Delegate.h */, BAFFCB551A92449F00C47F92 /* lzf */, BA12507F1A7338CD00A0532D /* AsyncBuffer.cc */, BA1250801A7338CD00A0532D /* AsyncBuffer.h */, BAFC840219C44AFD00C18B22 /* Buffer.h */, BAFC840419C44AFD00C18B22 /* ClientSocket.cc */, BAFC840519C44AFD00C18B22 /* ClientSocket.h */, BAFC840619C44AFD00C18B22 /* CMakeLists.txt */, BAFC840719C44AFD00C18B22 /* ConfigParser.cc */, BAFC840819C44AFD00C18B22 /* ConfigParser.h */, BAFC840919C44AFD00C18B22 /* EPoller.cc */, BAFC840A19C44AFD00C18B22 /* EPoller.h */, BAFC840B19C44AFD00C18B22 /* Kqueue.cc */, BAFC840C19C44AFD00C18B22 /* Kqueue.h */, BAFC840E19C44AFD00C18B22 /* ListenSocket.h */, BAFC840F19C44AFD00C18B22 /* Log */, BAFC842A19C44AFD00C18B22 /* NetThreadPool.cc */, BAFC842B19C44AFD00C18B22 /* NetThreadPool.h */, BAFC842C19C44AFD00C18B22 /* Poller.h */, BAFC842D19C44AFD00C18B22 /* Server.cc */, BAFC842E19C44AFD00C18B22 /* Server.h */, BAFC846019C44AFD00C18B22 /* Socket.cc */, BAFC846119C44AFD00C18B22 /* Socket.h */, BAFC846219C44AFD00C18B22 /* StreamSocket.cc */, BAFC840D19C44AFD00C18B22 /* ListenSocket.cc */, BAFC846319C44AFD00C18B22 /* StreamSocket.h */, BAFC846419C44AFD00C18B22 /* TaskManager.cc */, BAFC846519C44AFD00C18B22 /* TaskManager.h */, BAFC846619C44AFD00C18B22 /* Threads */, BAFC846E19C44AFD00C18B22 /* Timer.cc */, BAFC846F19C44AFD00C18B22 /* Timer.h */, BAFC847019C44AFD00C18B22 /* UnboundedBuffer.cc */, BAFC847119C44AFD00C18B22 /* UnboundedBuffer.h */, ); name = QBase; sourceTree = ""; }; BABEBECC19C4474900010636 = { isa = PBXGroup; children = ( BA34F9FD1D5C269D00F1DE01 /* Makefile */, BA99A7591CF19ED20051C43C /* qedis.conf */, BA34F9F11D5C1C9700F1DE01 /* README.md */, BAC9098D1B9D30DB002003C2 /* CMakeCommon */, BAC9098E1B9D30DB002003C2 /* CMakeLists.txt */, BA34F8DF1D5C198B00F1DE01 /* leveldb */, BAD0C61819E7A059005B3784 /* QedisSvr */, BAA158B219C44A0F007713F9 /* QBase */, BABEBED719C4474900010636 /* QedisCore */, BA6E478F1F9905C800EFBE6F /* QSentinel */, BA4CD2141CEB54BC00F68F67 /* Modules */, BADC7D7219D6BC700036858E /* UnitTest */, BABEBED619C4474900010636 /* Products */, ); sourceTree = ""; }; BABEBED619C4474900010636 /* Products */ = { isa = PBXGroup; children = ( BABEBED519C4474900010636 /* Qedis */, BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */, BADC7D7119D6BC700036858E /* UnitTest */, BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */, BA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */, BA34F6031D5C151700F1DE01 /* libleveldb.dylib */, BA6E47A51F99064D00EFBE6F /* libqcluster.dylib */, ); name = Products; sourceTree = ""; }; BABEBED719C4474900010636 /* QedisCore */ = { isa = PBXGroup; children = ( BAB2265C2007B7B7005241F6 /* QMigration.cc */, BAB2265D2007B7B7005241F6 /* QMigration.h */, BA6E478C1F99056000EFBE6F /* QSlaveClient.cc */, BA6E478D1F99056000EFBE6F /* QSlaveClient.h */, BA34F9F51D5C25E400F1DE01 /* QDumpInterface.h */, BA34F9F61D5C25E400F1DE01 /* QLeveldb.cc */, BA34F9F71D5C25E400F1DE01 /* QLeveldb.h */, BA4CD2361CEBF10600F68F67 /* CMakeLists.txt */, BA4CD2301CEB61E100F68F67 /* QModule.cc */, BA4CD2311CEB61E100F68F67 /* QModule.h */, BADD49B31CCA68BA0093257E /* QProtoParser.cc */, BADD49B41CCA68BA0093257E /* QProtoParser.h */, BA4053911BA1AE3B00B93053 /* QReplication.cc */, BA4053921BA1AE3B00B93053 /* QReplication.h */, BA98F83F1B1B539600ABCBD9 /* QSlowLog.cc */, BA98F8401B1B539600ABCBD9 /* QSlowLog.h */, BA98F83B1B01B16600ABCBD9 /* QConfig.cc */, BA98F83C1B01B16600ABCBD9 /* QConfig.h */, BA3B0AD41ACBA9E1000D9399 /* QAOF.cc */, BA3B0AD51ACBA9E1000D9399 /* QAOF.h */, BA47763B1AB0773F00EDBB5D /* redisIntset.c */, BA47763C1AB0773F00EDBB5D /* redisIntset.h */, BAFFCB601A9EA0AC00C47F92 /* redisZipList.c */, BAFFCB611A9EA0AC00C47F92 /* redisZipList.h */, BAFFCB5E1A9423DC00C47F92 /* crc64.c */, BAFFCB511A8F09CA00C47F92 /* QDB.cc */, BAFFCB521A8F09CA00C47F92 /* QDB.h */, BA29C44C19FB2CEE00796E3A /* QMulti.cc */, BA29C44D19FB2CEE00796E3A /* QMulti.h */, BA6C336A19EA77B200AC1800 /* QGlobRegex.cc */, BA6C336B19EA77B200AC1800 /* QGlobRegex.h */, BAD0C5E319E7A02C005B3784 /* QClient.cc */, BAD0C5E419E7A02C005B3784 /* QClient.h */, BAD0C5E519E7A02C005B3784 /* QCommand.cc */, BAD0C5E619E7A02C005B3784 /* QCommand.h */, BAD0C5E719E7A02C005B3784 /* QCommon.cc */, BAD0C5E819E7A02C005B3784 /* QCommon.h */, BAD0C5EA19E7A02C005B3784 /* QHash.cc */, BAD0C5EB19E7A02C005B3784 /* QHash.h */, BAD0C5EC19E7A02C005B3784 /* QKeyCommand.cc */, BAD0C5ED19E7A02C005B3784 /* QList.cc */, BAD0C5EE19E7A02C005B3784 /* QList.h */, BA6C337019EC0D9900AC1800 /* QPubsub.h */, BAD0C5EF19E7A02C005B3784 /* QPubsub.cc */, BAD0C5F019E7A02C005B3784 /* QServerCommand.cc */, BAD0C5F119E7A02C005B3784 /* QSet.cc */, BAD0C5F219E7A02C005B3784 /* QSet.h */, BAD0C5F319E7A02C005B3784 /* QSortedSet.cc */, BAD0C5F419E7A02C005B3784 /* QSortedSet.h */, BAD0C5F719E7A02C005B3784 /* QStore.cc */, BAD0C5F819E7A02C005B3784 /* QStore.h */, BAD0C5F919E7A02C005B3784 /* QString.cc */, BAD0C5FA19E7A02C005B3784 /* QString.h */, BAD0C5FB19E7A02C005B3784 /* QHelper.cc */, BAD0C5FC19E7A02C005B3784 /* QHelper.h */, ); name = QedisCore; path = Qedis; sourceTree = ""; }; BAD0C61819E7A059005B3784 /* QedisSvr */ = { isa = PBXGroup; children = ( BAFBDDB51CECAB2400E12FBA /* QedisLogo.h */, BA99CC521C52852C00BB88F7 /* Qedis.h */, BAD0C61C19E7A09F005B3784 /* CMakeLists.txt */, BAD0C61919E7A075005B3784 /* Qedis.cc */, ); name = QedisSvr; sourceTree = ""; }; BADC7D7219D6BC700036858E /* UnitTest */ = { isa = PBXGroup; children = ( BA6C336E19EA77C100AC1800 /* QGlobRegex_unittest.cc */, BAD0C62919E7A9D8005B3784 /* UnitTest.h */, BAD0C61D19E7A18B005B3784 /* CMakeLists.txt */, BADC7D7A19D6BC960036858E /* UnitTest.cc */, ); path = UnitTest; sourceTree = ""; }; BAFC840F19C44AFD00C18B22 /* Log */ = { isa = PBXGroup; children = ( BA12506F1A6D43D500A0532D /* MemoryFile.cc */, BA1250701A6D43D500A0532D /* MemoryFile.h */, BAFC841019C44AFD00C18B22 /* Logger.cc */, BAFC841119C44AFD00C18B22 /* Logger.h */, ); name = Log; path = QBase/Log; sourceTree = ""; }; BAFC846619C44AFD00C18B22 /* Threads */ = { isa = PBXGroup; children = ( BAFC846C19C44AFD00C18B22 /* ThreadPool.cc */, BAFC846D19C44AFD00C18B22 /* ThreadPool.h */, ); name = Threads; path = QBase/Threads; sourceTree = ""; }; BAFFCB551A92449F00C47F92 /* lzf */ = { isa = PBXGroup; children = ( BAFFCB561A9244B200C47F92 /* lzf_c.c */, BAFFCB571A9244B200C47F92 /* lzf_d.c */, BAFFCB581A9244B200C47F92 /* lzf.h */, BAFFCB591A9244B200C47F92 /* lzfP.h */, ); name = lzf; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ BA34F6011D5C151700F1DE01 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BA34F9A91D5C199D00F1DE01 /* env.h in Headers */, BA34F9EE1D5C199D00F1DE01 /* testharness.h in Headers */, BA34F9B61D5C199D00F1DE01 /* port.h in Headers */, BA34F98D1D5C199D00F1DE01 /* log_writer.h in Headers */, BA34F9D71D5C199D00F1DE01 /* coding.h in Headers */, BA34F9B51D5C199D00F1DE01 /* atomic_pointer.h in Headers */, BA34F9AB1D5C199D00F1DE01 /* iterator.h in Headers */, BA34F9E71D5C199D00F1DE01 /* logging.h in Headers */, BA34F9C71D5C199D00F1DE01 /* iterator_wrapper.h in Headers */, BA34F9EA1D5C199D00F1DE01 /* posix_logger.h in Headers */, BA34F9911D5C199D00F1DE01 /* skiplist.h in Headers */, BA34F9C21D5C199D00F1DE01 /* filter_block.h in Headers */, BA34F9DB1D5C199D00F1DE01 /* crc32c.h in Headers */, BA34F9A61D5C199D00F1DE01 /* comparator.h in Headers */, BA34F9AA1D5C199D00F1DE01 /* filter_policy.h in Headers */, BA34F9F01D5C199D00F1DE01 /* testutil.h in Headers */, BA34F9BA1D5C199D00F1DE01 /* thread_annotations.h in Headers */, BA34F9A41D5C199D00F1DE01 /* c.h in Headers */, BA34F9E51D5C199D00F1DE01 /* histogram.h in Headers */, BA34F98A1D5C199D00F1DE01 /* log_reader.h in Headers */, BA34F97B1D5C199D00F1DE01 /* db_impl.h in Headers */, BA34F97D1D5C199D00F1DE01 /* db_iter.h in Headers */, BA34F9C51D5C199D00F1DE01 /* format.h in Headers */, BA34F98F1D5C199D00F1DE01 /* memtable.h in Headers */, BA34F9B11D5C199D00F1DE01 /* write_batch.h in Headers */, BA34F9C01D5C199D00F1DE01 /* block_builder.h in Headers */, BA34F9D01D5C199D00F1DE01 /* arena.h in Headers */, BA34F9AC1D5C199D00F1DE01 /* options.h in Headers */, BA34F9A21D5C199D00F1DE01 /* memenv.h in Headers */, BA34F99A1D5C199D00F1DE01 /* version_set.h in Headers */, BA34F9BE1D5C199D00F1DE01 /* block.h in Headers */, BA34F9E21D5C199D00F1DE01 /* hash.h in Headers */, BA34F9A51D5C199D00F1DE01 /* cache.h in Headers */, BA34F9E81D5C199D00F1DE01 /* mutexlock.h in Headers */, BA34F9CE1D5C199D00F1DE01 /* two_level_iterator.h in Headers */, BA34F9951D5C199D00F1DE01 /* table_cache.h in Headers */, BA34F9B91D5C199D00F1DE01 /* port_posix.h in Headers */, BA34F9881D5C199D00F1DE01 /* log_format.h in Headers */, BA34F9AF1D5C199D00F1DE01 /* table.h in Headers */, BA34F9B71D5C199D00F1DE01 /* port_example.h in Headers */, BA34F9851D5C199D00F1DE01 /* filename.h in Headers */, BA34F9971D5C199D00F1DE01 /* version_edit.h in Headers */, BA34F9C91D5C199D00F1DE01 /* merger.h in Headers */, BA34F99D1D5C199D00F1DE01 /* write_batch_internal.h in Headers */, BA34F9AE1D5C199D00F1DE01 /* status.h in Headers */, BA34F9A71D5C199D00F1DE01 /* db.h in Headers */, BA34F9751D5C199D00F1DE01 /* builder.h in Headers */, BA34F9931D5C199D00F1DE01 /* snapshot.h in Headers */, BA34F9EB1D5C199D00F1DE01 /* random.h in Headers */, BA34F9B01D5C199D00F1DE01 /* table_builder.h in Headers */, BA34F9AD1D5C199D00F1DE01 /* slice.h in Headers */, BA34F9A81D5C199D00F1DE01 /* dumpfile.h in Headers */, BA34F9801D5C199D00F1DE01 /* dbformat.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; BA4CD2171CEB54F000F68F67 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BA4CD2291CEB54FF00F68F67 /* QListModule.h in Headers */, BA4CD22D1CEB54FF00F68F67 /* QSetModule.h in Headers */, BA4CD2271CEB54FF00F68F67 /* QHashModule.h in Headers */, BA4CD22B1CEB54FF00F68F67 /* QModuleInit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; BA6E47A31F99064D00EFBE6F /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BA6E47B31F9906A400EFBE6F /* proto.h in Headers */, BA6E47B41F9906A400EFBE6F /* recordio.h in Headers */, BA6E47B51F9906A400EFBE6F /* zookeeper_version.h in Headers */, BA6E47B61F9906A400EFBE6F /* zookeeper.jute.h in Headers */, BA6E47B71F9906A400EFBE6F /* ZookeeperConn.h in Headers */, BA6E47B11F9906A400EFBE6F /* QClusterClient.h in Headers */, BA6E47B21F9906A400EFBE6F /* QClusterInterface.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; BAD0C5D719E79FF7005B3784 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BAD0C60F19E7A02C005B3784 /* QSortedSet.h in Headers */, BAD0C60919E7A02C005B3784 /* QList.h in Headers */, BADD49B61CCA68BA0093257E /* QProtoParser.h in Headers */, BA6C336D19EA77B200AC1800 /* QGlobRegex.h in Headers */, BA3B0AD71ACBA9E1000D9399 /* QAOF.h in Headers */, BA47763E1AB0773F00EDBB5D /* redisIntset.h in Headers */, BAD0C61519E7A02C005B3784 /* QString.h in Headers */, BA34F9FA1D5C25E400F1DE01 /* QLeveldb.h in Headers */, BAD0C60D19E7A02C005B3784 /* QSet.h in Headers */, BAD0C60419E7A02C005B3784 /* QCommon.h in Headers */, BAD0C61719E7A02C005B3784 /* QHelper.h in Headers */, BAD0C60219E7A02C005B3784 /* QCommand.h in Headers */, BAD0C60619E7A02C005B3784 /* QHash.h in Headers */, BAFFCB631A9EA0AC00C47F92 /* redisZipList.h in Headers */, BAFFCB541A8F09CA00C47F92 /* QDB.h in Headers */, BAD0C61319E7A02C005B3784 /* QStore.h in Headers */, BA98F8421B1B539600ABCBD9 /* QSlowLog.h in Headers */, BA4053941BA1AE3B00B93053 /* QReplication.h in Headers */, BA29C44F19FB2CEE00796E3A /* QMulti.h in Headers */, BA4CD2331CEB61E100F68F67 /* QModule.h in Headers */, BA98F83E1B01B16600ABCBD9 /* QConfig.h in Headers */, BA34F9F81D5C25E400F1DE01 /* QDumpInterface.h in Headers */, BAD0C60019E7A02C005B3784 /* QClient.h in Headers */, BA6C337119EC0D9900AC1800 /* QPubsub.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; BAFC83FC19C44AE800C18B22 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BAFC84BC19C44AFD00C18B22 /* TaskManager.h in Headers */, BA2C0C7B1CDC1DC800BDF050 /* Delegate.h in Headers */, BAFC84C519C44AFD00C18B22 /* Timer.h in Headers */, BAFC84C719C44AFD00C18B22 /* UnboundedBuffer.h in Headers */, BAFFCB5C1A9244B200C47F92 /* lzf.h in Headers */, BAFC84BA19C44AFD00C18B22 /* StreamSocket.h in Headers */, BAFC84C319C44AFD00C18B22 /* ThreadPool.h in Headers */, BAFC847419C44AFD00C18B22 /* ClientSocket.h in Headers */, BAFC847E19C44AFD00C18B22 /* Logger.h in Headers */, BAFC847A19C44AFD00C18B22 /* Kqueue.h in Headers */, BAFC84B819C44AFD00C18B22 /* Socket.h in Headers */, BAFC848F19C44AFD00C18B22 /* NetThreadPool.h in Headers */, BAFC847219C44AFD00C18B22 /* Buffer.h in Headers */, BA1250721A6D43D500A0532D /* MemoryFile.h in Headers */, BA1250821A7338CD00A0532D /* AsyncBuffer.h in Headers */, BAFC847819C44AFD00C18B22 /* EPoller.h in Headers */, BAFC849019C44AFD00C18B22 /* Poller.h in Headers */, BAFC849219C44AFD00C18B22 /* Server.h in Headers */, BAFFCB5D1A9244B200C47F92 /* lzfP.h in Headers */, BAFC847C19C44AFD00C18B22 /* ListenSocket.h in Headers */, BAFC847619C44AFD00C18B22 /* ConfigParser.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ BA34F6021D5C151700F1DE01 /* leveldb */ = { isa = PBXNativeTarget; buildConfigurationList = BA34F6041D5C151800F1DE01 /* Build configuration list for PBXNativeTarget "leveldb" */; buildPhases = ( BA8B10761F7E691A004F99D0 /* ShellScript */, BA34F5FF1D5C151700F1DE01 /* Sources */, BA34F6001D5C151700F1DE01 /* Frameworks */, BA34F6011D5C151700F1DE01 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = leveldb; productName = leveldb; productReference = BA34F6031D5C151700F1DE01 /* libleveldb.dylib */; productType = "com.apple.product-type.library.dynamic"; }; BA4CD2181CEB54F000F68F67 /* qedismodule */ = { isa = PBXNativeTarget; buildConfigurationList = BA4CD21A1CEB54F000F68F67 /* Build configuration list for PBXNativeTarget "qedismodule" */; buildPhases = ( BA4CD2151CEB54F000F68F67 /* Sources */, BA4CD2161CEB54F000F68F67 /* Frameworks */, BA4CD2171CEB54F000F68F67 /* Headers */, ); buildRules = ( ); dependencies = ( BA4CD22F1CEB55B400F68F67 /* PBXTargetDependency */, ); name = qedismodule; productName = qedismodule; productReference = BA4CD2191CEB54F000F68F67 /* libqedismodule.dylib */; productType = "com.apple.product-type.library.dynamic"; }; BA6E47A41F99064D00EFBE6F /* qcluster */ = { isa = PBXNativeTarget; buildConfigurationList = BA6E47A61F99064D00EFBE6F /* Build configuration list for PBXNativeTarget "qcluster" */; buildPhases = ( BA6E47A11F99064D00EFBE6F /* Sources */, BA6E47A21F99064D00EFBE6F /* Frameworks */, BA6E47A31F99064D00EFBE6F /* Headers */, ); buildRules = ( ); dependencies = ( BA6E47AA1F99066700EFBE6F /* PBXTargetDependency */, BA6E47AC1F99066700EFBE6F /* PBXTargetDependency */, ); name = qcluster; productName = qcluster; productReference = BA6E47A51F99064D00EFBE6F /* libqcluster.dylib */; productType = "com.apple.product-type.library.dynamic"; }; BABEBED419C4474900010636 /* Qedis */ = { isa = PBXNativeTarget; buildConfigurationList = BABEBEDE19C4474900010636 /* Build configuration list for PBXNativeTarget "Qedis" */; buildPhases = ( BABEBED119C4474900010636 /* Sources */, BABEBED219C4474900010636 /* Frameworks */, BABEBED319C4474900010636 /* CopyFiles */, ); buildRules = ( ); dependencies = ( BA34F8DE1D5C191300F1DE01 /* PBXTargetDependency */, BAD0C5E019E7A007005B3784 /* PBXTargetDependency */, BAFC84D519C44B6800C18B22 /* PBXTargetDependency */, ); name = Qedis; productName = Qedis; productReference = BABEBED519C4474900010636 /* Qedis */; productType = "com.apple.product-type.tool"; }; BAD0C5D819E79FF7005B3784 /* qedislib */ = { isa = PBXNativeTarget; buildConfigurationList = BAD0C5DA19E79FF7005B3784 /* Build configuration list for PBXNativeTarget "qedislib" */; buildPhases = ( BAD0C5D519E79FF7005B3784 /* Sources */, BAD0C5D619E79FF7005B3784 /* Frameworks */, BAD0C5D719E79FF7005B3784 /* Headers */, ); buildRules = ( ); dependencies = ( BA34F8DC1D5C190900F1DE01 /* PBXTargetDependency */, BAD0C5DE19E7A002005B3784 /* PBXTargetDependency */, ); name = qedislib; productName = qedislib; productReference = BAD0C5D919E79FF7005B3784 /* libqedislib.dylib */; productType = "com.apple.product-type.library.static"; }; BADC7D7019D6BC700036858E /* UnitTest */ = { isa = PBXNativeTarget; buildConfigurationList = BADC7D7719D6BC700036858E /* Build configuration list for PBXNativeTarget "UnitTest" */; buildPhases = ( BADC7D6D19D6BC700036858E /* Sources */, BADC7D6E19D6BC700036858E /* Frameworks */, BADC7D6F19D6BC700036858E /* CopyFiles */, ); buildRules = ( ); dependencies = ( BAD0C62319E7A88D005B3784 /* PBXTargetDependency */, BAD0C62119E7A889005B3784 /* PBXTargetDependency */, ); name = UnitTest; productName = UnitTest; productReference = BADC7D7119D6BC700036858E /* UnitTest */; productType = "com.apple.product-type.tool"; }; BAFC83FD19C44AE800C18B22 /* qbaselib */ = { isa = PBXNativeTarget; buildConfigurationList = BAFC840119C44AE800C18B22 /* Build configuration list for PBXNativeTarget "qbaselib" */; buildPhases = ( BAFC83FA19C44AE800C18B22 /* Sources */, BAFC83FB19C44AE800C18B22 /* Frameworks */, BAFC83FC19C44AE800C18B22 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = qbaselib; productName = qbaselib; productReference = BAFC83FE19C44AE800C18B22 /* libqbaselib.dylib */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ BABEBECD19C4474900010636 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0610; ORGANIZATIONNAME = "Bert Young"; TargetAttributes = { BA34F6021D5C151700F1DE01 = { CreatedOnToolsVersion = 6.1.1; }; BA4CD2181CEB54F000F68F67 = { CreatedOnToolsVersion = 6.1.1; }; BA6E47A41F99064D00EFBE6F = { CreatedOnToolsVersion = 8.3.3; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = BABEBED019C4474900010636 /* Build configuration list for PBXProject "Qedis" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = BABEBECC19C4474900010636; productRefGroup = BABEBED619C4474900010636 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( BABEBED419C4474900010636 /* Qedis */, BAFC83FD19C44AE800C18B22 /* qbaselib */, BADC7D7019D6BC700036858E /* UnitTest */, BAD0C5D819E79FF7005B3784 /* qedislib */, BA4CD2181CEB54F000F68F67 /* qedismodule */, BA34F6021D5C151700F1DE01 /* leveldb */, BA6E47A41F99064D00EFBE6F /* qcluster */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ BA8B10761F7E691A004F99D0 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "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"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ BA34F5FF1D5C151700F1DE01 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA34F9CD1D5C199D00F1DE01 /* two_level_iterator.cc in Sources */, BA34F9EF1D5C199D00F1DE01 /* testutil.cc in Sources */, BA34F99C1D5C199D00F1DE01 /* write_batch.cc in Sources */, BA34F97F1D5C199D00F1DE01 /* dbformat.cc in Sources */, BA34F9C11D5C199D00F1DE01 /* filter_block.cc in Sources */, BA34F9841D5C199D00F1DE01 /* filename.cc in Sources */, BA34F9A11D5C199D00F1DE01 /* memenv.cc in Sources */, BA34F9C81D5C199D00F1DE01 /* merger.cc in Sources */, BA34F9CA1D5C199D00F1DE01 /* table.cc in Sources */, BA34F9DA1D5C199D00F1DE01 /* crc32c.cc in Sources */, BA34F9821D5C199D00F1DE01 /* dumpfile.cc in Sources */, BA34F9961D5C199D00F1DE01 /* version_edit.cc in Sources */, BA34F9C61D5C199D00F1DE01 /* iterator.cc in Sources */, BA34F98C1D5C199D00F1DE01 /* log_writer.cc in Sources */, BA34F9DE1D5C199D00F1DE01 /* env_posix.cc in Sources */, BA34F9E41D5C199D00F1DE01 /* histogram.cc in Sources */, BA34F9BD1D5C199D00F1DE01 /* block.cc in Sources */, BA34F9E91D5C199D00F1DE01 /* options.cc in Sources */, BA34F9ED1D5C199D00F1DE01 /* testharness.cc in Sources */, BA34F9D61D5C199D00F1DE01 /* coding.cc in Sources */, BA34F9D91D5C199D00F1DE01 /* comparator.cc in Sources */, BA34F9CF1D5C199D00F1DE01 /* arena.cc in Sources */, BA34F9891D5C199D00F1DE01 /* log_reader.cc in Sources */, BA34F9741D5C199D00F1DE01 /* builder.cc in Sources */, BA34F9CB1D5C199D00F1DE01 /* table_builder.cc in Sources */, BA34F9941D5C199D00F1DE01 /* table_cache.cc in Sources */, BA34F9761D5C199D00F1DE01 /* c.cc in Sources */, BA34F9D41D5C199D00F1DE01 /* cache.cc in Sources */, BA34F97A1D5C199D00F1DE01 /* db_impl.cc in Sources */, BA34F9E61D5C199D00F1DE01 /* logging.cc in Sources */, BA34F9BF1D5C199D00F1DE01 /* block_builder.cc in Sources */, BA34F9901D5C199D00F1DE01 /* repair.cc in Sources */, BA34F9D21D5C199D00F1DE01 /* bloom.cc in Sources */, BA34F9791D5C199D00F1DE01 /* db_bench.cc in Sources */, BA34F9DD1D5C199D00F1DE01 /* env.cc in Sources */, BA34F9B81D5C199D00F1DE01 /* port_posix.cc in Sources */, BA34F9C41D5C199D00F1DE01 /* format.cc in Sources */, BA34F9EC1D5C199D00F1DE01 /* status.cc in Sources */, BA34F9E01D5C199D00F1DE01 /* filter_policy.cc in Sources */, BA34F9991D5C199D00F1DE01 /* version_set.cc in Sources */, BA34F9E11D5C199D00F1DE01 /* hash.cc in Sources */, BA34F98E1D5C199D00F1DE01 /* memtable.cc in Sources */, BA34F97C1D5C199D00F1DE01 /* db_iter.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BA4CD2151CEB54F000F68F67 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA4CD22C1CEB54FF00F68F67 /* QSetModule.cc in Sources */, BA4CD22A1CEB54FF00F68F67 /* QModuleInit.cc in Sources */, BA4CD2261CEB54FF00F68F67 /* QHashModule.cc in Sources */, BA4CD2281CEB54FF00F68F67 /* QListModule.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BA6E47A11F99064D00EFBE6F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA6E47AD1F99068A00EFBE6F /* QClusterClient.cc in Sources */, BA6E47AE1F99068A00EFBE6F /* recordio.c in Sources */, BA6E47AF1F99068A00EFBE6F /* zookeeper.jute.c in Sources */, BA6E47B01F99068A00EFBE6F /* ZookeeperConn.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BABEBED119C4474900010636 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA6E47A01F9905F500EFBE6F /* ZookeeperConn.cc in Sources */, BAB2265E2007B7B7005241F6 /* QMigration.cc in Sources */, BA6E479F1F9905F500EFBE6F /* zookeeper.jute.c in Sources */, BA6E478E1F99056000EFBE6F /* QSlaveClient.cc in Sources */, BA6E47951F9905E600EFBE6F /* QClusterClient.cc in Sources */, BA6E479E1F9905F500EFBE6F /* recordio.c in Sources */, BAD0C61A19E7A075005B3784 /* Qedis.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BAD0C5D519E79FF7005B3784 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA4CD2321CEB61E100F68F67 /* QModule.cc in Sources */, BA47763D1AB0773F00EDBB5D /* redisIntset.c in Sources */, BA98F8411B1B539600ABCBD9 /* QSlowLog.cc in Sources */, BA4053931BA1AE3B00B93053 /* QReplication.cc in Sources */, BAD0C61619E7A02C005B3784 /* QHelper.cc in Sources */, BA6C336C19EA77B200AC1800 /* QGlobRegex.cc in Sources */, BA3B0AD61ACBA9E1000D9399 /* QAOF.cc in Sources */, BAD0C61219E7A02C005B3784 /* QStore.cc in Sources */, BA98F83D1B01B16600ABCBD9 /* QConfig.cc in Sources */, BAFFCB531A8F09CA00C47F92 /* QDB.cc in Sources */, BAFFCB5F1A9423DC00C47F92 /* crc64.c in Sources */, BAD0C60C19E7A02C005B3784 /* QSet.cc in Sources */, BAD0C60A19E7A02C005B3784 /* QPubsub.cc in Sources */, BAD0C60519E7A02C005B3784 /* QHash.cc in Sources */, BAD0C60319E7A02C005B3784 /* QCommon.cc in Sources */, BAFFCB621A9EA0AC00C47F92 /* redisZipList.c in Sources */, BAD0C5FF19E7A02C005B3784 /* QClient.cc in Sources */, BAD0C61419E7A02C005B3784 /* QString.cc in Sources */, BAD0C60719E7A02C005B3784 /* QKeyCommand.cc in Sources */, BAD0C60819E7A02C005B3784 /* QList.cc in Sources */, BA34F9F91D5C25E400F1DE01 /* QLeveldb.cc in Sources */, BAD0C60E19E7A02C005B3784 /* QSortedSet.cc in Sources */, BA29C44E19FB2CEE00796E3A /* QMulti.cc in Sources */, BADD49B51CCA68BA0093257E /* QProtoParser.cc in Sources */, BAD0C60B19E7A02C005B3784 /* QServerCommand.cc in Sources */, BAD0C60119E7A02C005B3784 /* QCommand.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BADC7D6D19D6BC700036858E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BA6C336F19EA77C100AC1800 /* QGlobRegex_unittest.cc in Sources */, BADC7D7C19D6BC960036858E /* UnitTest.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BAFC83FA19C44AE800C18B22 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BAFC847519C44AFD00C18B22 /* ConfigParser.cc in Sources */, BAFFCB5A1A9244B200C47F92 /* lzf_c.c in Sources */, BAFC84C219C44AFD00C18B22 /* ThreadPool.cc in Sources */, BAFC847919C44AFD00C18B22 /* Kqueue.cc in Sources */, BA1250711A6D43D500A0532D /* MemoryFile.cc in Sources */, BAFC849119C44AFD00C18B22 /* Server.cc in Sources */, BAFC848E19C44AFD00C18B22 /* NetThreadPool.cc in Sources */, BAFC84B719C44AFD00C18B22 /* Socket.cc in Sources */, BAFC847319C44AFD00C18B22 /* ClientSocket.cc in Sources */, BAFC847719C44AFD00C18B22 /* EPoller.cc in Sources */, BAFFCB5B1A9244B200C47F92 /* lzf_d.c in Sources */, BAFC847B19C44AFD00C18B22 /* ListenSocket.cc in Sources */, BAFC84C619C44AFD00C18B22 /* UnboundedBuffer.cc in Sources */, BA1250811A7338CD00A0532D /* AsyncBuffer.cc in Sources */, BAFC84BB19C44AFD00C18B22 /* TaskManager.cc in Sources */, BAFC84B919C44AFD00C18B22 /* StreamSocket.cc in Sources */, BAFC84C419C44AFD00C18B22 /* Timer.cc in Sources */, BAFC847D19C44AFD00C18B22 /* Logger.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ BA34F8DC1D5C190900F1DE01 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BA34F6021D5C151700F1DE01 /* leveldb */; targetProxy = BA34F8DB1D5C190900F1DE01 /* PBXContainerItemProxy */; }; BA34F8DE1D5C191300F1DE01 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BA34F6021D5C151700F1DE01 /* leveldb */; targetProxy = BA34F8DD1D5C191300F1DE01 /* PBXContainerItemProxy */; }; BA4CD22F1CEB55B400F68F67 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAD0C5D819E79FF7005B3784 /* qedislib */; targetProxy = BA4CD22E1CEB55B400F68F67 /* PBXContainerItemProxy */; }; BA6E47AA1F99066700EFBE6F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAFC83FD19C44AE800C18B22 /* qbaselib */; targetProxy = BA6E47A91F99066700EFBE6F /* PBXContainerItemProxy */; }; BA6E47AC1F99066700EFBE6F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAD0C5D819E79FF7005B3784 /* qedislib */; targetProxy = BA6E47AB1F99066700EFBE6F /* PBXContainerItemProxy */; }; BAD0C5DE19E7A002005B3784 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAFC83FD19C44AE800C18B22 /* qbaselib */; targetProxy = BAD0C5DD19E7A002005B3784 /* PBXContainerItemProxy */; }; BAD0C5E019E7A007005B3784 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAD0C5D819E79FF7005B3784 /* qedislib */; targetProxy = BAD0C5DF19E7A007005B3784 /* PBXContainerItemProxy */; }; BAD0C62119E7A889005B3784 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAFC83FD19C44AE800C18B22 /* qbaselib */; targetProxy = BAD0C62019E7A889005B3784 /* PBXContainerItemProxy */; }; BAD0C62319E7A88D005B3784 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAD0C5D819E79FF7005B3784 /* qedislib */; targetProxy = BAD0C62219E7A88D005B3784 /* PBXContainerItemProxy */; }; BAFC84D519C44B6800C18B22 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAFC83FD19C44AE800C18B22 /* qbaselib */; targetProxy = BAFC84D419C44B6800C18B22 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ BA34F6051D5C151800F1DE01 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = lib; GCC_PREPROCESSOR_DEFINITIONS = ( LEVELDB_PLATFORM_POSIX, "DEBUG=1", "$(inherited)", OS_MACOSX, ); GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/leveldb", ); MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BA34F6061D5C151800F1DE01 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = lib; GCC_PREPROCESSOR_DEFINITIONS = ( LEVELDB_PLATFORM_POSIX, OS_MACOSX, ); GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/leveldb", ); MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BA4CD21B1CEB54F000F68F67 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = lib; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BA4CD21C1CEB54F000F68F67 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = lib; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BA6E47A71F99064D00EFBE6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_IDENTITY = "-"; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; EXECUTABLE_PREFIX = lib; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BA6E47A81F99064D00EFBE6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = lib; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BABEBEDC19C4474900010636 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", leveldb, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, leveldb/include, ); MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; BABEBEDD19C4474900010636 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", leveldb, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, leveldb/include, ); MACOSX_DEPLOYMENT_TARGET = 10.9; SDKROOT = macosx; }; name = Release; }; BABEBEDF19C4474900010636 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QBase", ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BABEBEE019C4474900010636 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QBase", ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BAD0C5DB19E79FF7005B3784 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_EXTENSION = dylib; EXECUTABLE_PREFIX = lib; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QBase", ); MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BAD0C5DC19E79FF7005B3784 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_EXTENSION = dylib; EXECUTABLE_PREFIX = lib; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QBase", ); MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BADC7D7819D6BC700036858E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QedisCore", ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BADC7D7919D6BC700036858E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/QedisCore", ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; BAFC83FF19C44AE800C18B22 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_EXTENSION = dylib; EXECUTABLE_PREFIX = lib; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BAFC840019C44AE800C18B22 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_EXTENSION = dylib; EXECUTABLE_PREFIX = lib; MACH_O_TYPE = mh_dylib; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ BA34F6041D5C151800F1DE01 /* Build configuration list for PBXNativeTarget "leveldb" */ = { isa = XCConfigurationList; buildConfigurations = ( BA34F6051D5C151800F1DE01 /* Debug */, BA34F6061D5C151800F1DE01 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BA4CD21A1CEB54F000F68F67 /* Build configuration list for PBXNativeTarget "qedismodule" */ = { isa = XCConfigurationList; buildConfigurations = ( BA4CD21B1CEB54F000F68F67 /* Debug */, BA4CD21C1CEB54F000F68F67 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BA6E47A61F99064D00EFBE6F /* Build configuration list for PBXNativeTarget "qcluster" */ = { isa = XCConfigurationList; buildConfigurations = ( BA6E47A71F99064D00EFBE6F /* Debug */, BA6E47A81F99064D00EFBE6F /* Release */, ); defaultConfigurationIsVisible = 0; }; BABEBED019C4474900010636 /* Build configuration list for PBXProject "Qedis" */ = { isa = XCConfigurationList; buildConfigurations = ( BABEBEDC19C4474900010636 /* Debug */, BABEBEDD19C4474900010636 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BABEBEDE19C4474900010636 /* Build configuration list for PBXNativeTarget "Qedis" */ = { isa = XCConfigurationList; buildConfigurations = ( BABEBEDF19C4474900010636 /* Debug */, BABEBEE019C4474900010636 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BAD0C5DA19E79FF7005B3784 /* Build configuration list for PBXNativeTarget "qedislib" */ = { isa = XCConfigurationList; buildConfigurations = ( BAD0C5DB19E79FF7005B3784 /* Debug */, BAD0C5DC19E79FF7005B3784 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BADC7D7719D6BC700036858E /* Build configuration list for PBXNativeTarget "UnitTest" */ = { isa = XCConfigurationList; buildConfigurations = ( BADC7D7819D6BC700036858E /* Debug */, BADC7D7919D6BC700036858E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BAFC840119C44AE800C18B22 /* Build configuration list for PBXNativeTarget "qbaselib" */ = { isa = XCConfigurationList; buildConfigurations = ( BAFC83FF19C44AE800C18B22 /* Debug */, BAFC840019C44AE800C18B22 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = BABEBECD19C4474900010636 /* Project object */; } ================================================ FILE: QedisCore/CMakeLists.txt ================================================ AUX_SOURCE_DIRECTORY(. QEDIS_SRC) INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) LINK_DIRECTORIES(../../leveldb) ADD_LIBRARY(qediscore SHARED ${QEDIS_SRC}) SET(LIBRARY_OUTPUT_PATH ../../bin) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/leveldb/include) ADD_DEPENDENCIES(qediscore qbaselib leveldb) TARGET_LINK_LIBRARIES(qediscore; qbaselib; dl; leveldb) SET_TARGET_PROPERTIES(qediscore PROPERTIES LINKER_LANGUAGE CXX) ================================================ FILE: QedisCore/QAOF.cc ================================================ #include "QAOF.h" #include "Log/Logger.h" #include "Threads/ThreadPool.h" #include "QStore.h" #include "QConfig.h" #include "QProtoParser.h" #include #include namespace qedis { const char* const g_aofTmp = "qedis_appendonly.aof.tmp"; pid_t g_rewritePid = -1; /***************************************************** * when after fork(), the parent stop aof thread, which means * the coming aof data will be write to the tmp buffer, not to * aof file. ****************************************************/ void QAOFThreadController::RewriteDoneHandler(int exitRet, int whatSignal) { g_rewritePid = -1; if (exitRet == 0 && whatSignal == 0) { INF << "save aof success"; ::rename(g_aofTmp, g_config.appendfilename.c_str()); } else { ERR << "save aof failed with exit result " << exitRet << ", signal " << whatSignal; ::unlink(g_aofTmp); } QAOFThreadController::Instance().Start(); } QAOFThreadController& QAOFThreadController::Instance() { static QAOFThreadController threadCtrl; return threadCtrl; } bool QAOFThreadController::ProcessTmpBuffer(BufferSequence& bf) { aofBuffer_.ProcessBuffer(bf); return bf.count > 0; } void QAOFThreadController::SkipTmpBuffer(size_t n) { aofBuffer_.Skip(n); } // main thread call this void QAOFThreadController::Start() { DBG << "start aof thread"; assert(!aofThread_ || !aofThread_->IsAlive()); aofThread_ = std::make_shared(); aofThread_->SetAlive(); ThreadPool::Instance().ExecuteTask(std::bind(&AOFThread::Run, aofThread_)); } // when fork(), parent call stop; void QAOFThreadController::Stop() { if (!aofThread_) return; DBG << "stop aof thread"; aofThread_->Stop(); QAOFThreadController::Instance().Join(); aofThread_ = nullptr; } // main thread call this void QAOFThreadController::_WriteSelectDB(int db, AsyncBuffer& dst) { if (db == lastDb_) return; lastDb_ = db; WriteMultiBulkLong(2, dst); WriteBulkString("select", 6, dst); WriteBulkLong(db, dst); } void QAOFThreadController::SaveCommand(const std::vector& params, int db) { AsyncBuffer* dst; if (aofThread_ && aofThread_->IsAlive()) dst = &aofThread_->buf_; else dst = &aofBuffer_; _WriteSelectDB(db, *dst); qedis::SaveCommand(params, *dst); } QAOFThreadController::AOFThread::~AOFThread() { file_.Close(); } bool QAOFThreadController::AOFThread::Flush() { BufferSequence data; buf_.ProcessBuffer(data); if (data.count == 0) return false; if (!file_.IsOpen()) file_.Open(g_config.appendfilename.c_str()); for (size_t i = 0; i < data.count; ++ i) { file_.Write(data.buffers[i].iov_base, data.buffers[i].iov_len); } buf_.Skip(data.TotalBytes()); return data.count != 0; } void QAOFThreadController::AOFThread::SaveCommand(const std::vector& params) { qedis::SaveCommand(params, buf_); } void QAOFThreadController::AOFThread::Run() { assert (IsAlive()); // CHECK aof temp buffer first! BufferSequence data; while (QAOFThreadController::Instance().ProcessTmpBuffer(data)) { if (!file_.IsOpen()) file_.Open(g_config.appendfilename.c_str()); for (size_t i = 0; i < data.count; ++ i) { file_.Write(data.buffers[i].iov_base, data.buffers[i].iov_len); } QAOFThreadController::Instance().SkipTmpBuffer(data.TotalBytes()); } while (IsAlive()) { //sync incrementally, always, the redis sync policy is useless if (Flush()) file_.Sync(); else std::this_thread::sleep_for(std::chrono::milliseconds(1)); } file_.Close(); pro_.set_value(); } void QAOFThreadController::Join() { if (aofThread_) aofThread_->pro_.get_future().wait(); } static void SaveExpire(const QString& key, uint64_t absMs, OutputMemoryFile& file) { WriteBulkLong(3, file); WriteBulkString("expire", 6, file); WriteBulkString(key, file); WriteBulkLong(absMs, file); } // child save the db to tmp file static void SaveObject(const QString& key, const QObject& obj, OutputMemoryFile& file); static void RewriteProcess() { OutputMemoryFile file; if (!file.Open(g_aofTmp, false)) { perror("open tmp failed"); _exit(-1); } for (int dbno = 0; true; ++ dbno) { if (QSTORE.SelectDB(dbno) == -1) break; if (QSTORE.DBSize() == 0) continue; // select db WriteMultiBulkLong(2, file); WriteBulkString("select", 6, file); WriteBulkLong(dbno, file); const auto now = ::Now(); for (const auto& kv : QSTORE) { int64_t ttl = QSTORE.TTL(kv.first, now); if (ttl == QStore::ExpireResult::expired) continue; SaveObject(kv.first, kv.second, file); if (ttl > 0) SaveExpire(kv.first, ttl + now, file); } } } QError bgrewriteaof(const std::vector& , UnboundedBuffer* reply) { if (g_rewritePid != -1) { reply->PushData("-ERR aof already in progress\r\n", sizeof "-ERR aof already in progress\r\n" - 1); return QError_ok; } else { g_rewritePid = fork(); switch (g_rewritePid) { case 0: RewriteProcess(); _exit(0); case -1: ERR << "fork aof process failed, errno = " << errno; reply->PushData("-ERR aof rewrite failed\r\n", sizeof "-ERR aof rewrite failed\r\n" - 1); return QError_ok; default: break; } } QAOFThreadController::Instance().Stop(); FormatOK(reply); return QError_ok; } static void SaveStringObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { WriteMultiBulkLong(3, file); WriteBulkString("set", 3, file); WriteBulkString(key, file); auto str = GetDecodedString(&obj); WriteBulkString(*str, file); } static void SaveListObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { auto list = obj.CastList(); if (list->empty()) return; WriteMultiBulkLong(list->size() + 2, file); // rpush listname + elems WriteBulkString("rpush", 5, file); WriteBulkString(key, file); for (const auto& elem : *list) { WriteBulkString(elem, file); } } static void SaveSetObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { auto set = obj.CastSet(); if (set->empty()) return; WriteMultiBulkLong(set->size() + 2, file); // sadd set_name + elems WriteBulkString("sadd", 4, file); WriteBulkString(key, file); for (const auto& elem : *set) { WriteBulkString(elem, file); } } static void SaveZSetObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { auto zset = obj.CastSortedSet(); if (zset->Size() == 0) return; WriteMultiBulkLong(2 * zset->Size() + 2, file); // zadd zset_name + (score + member) WriteBulkString("zadd", 4, file); WriteBulkString(key, file); for (const auto& pair : *zset) { const QString& member = pair.first; double score = pair.second; char scoreStr[32]; int len = Double2Str(scoreStr, sizeof scoreStr, score); WriteBulkString(scoreStr, len, file); WriteBulkString(member, file); } } static void SaveHashObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { auto hash = obj.CastHash(); if (hash->empty()) return; WriteMultiBulkLong(2* hash->size() + 2, file); // hmset hash_name + (key + value) WriteBulkString("hmset", 5, file); WriteBulkString(key, file); for (const auto& pair : *hash) { WriteBulkString(pair.first, file); WriteBulkString(pair.second, file); } } static void SaveObject(const QString& key, const QObject& obj, OutputMemoryFile& file) { switch (obj.type) { case QType_string: SaveStringObject(key, obj, file); break; case QType_list: SaveListObject(key, obj, file); break; case QType_set: SaveSetObject(key, obj, file); break; case QType_sortedSet: SaveZSetObject(key, obj, file); break; case QType_hash: SaveHashObject(key, obj, file); break; default: assert (false); break; } } QAOFLoader::QAOFLoader() { } bool QAOFLoader::Load(const char* name) { if (::access(name, F_OK) != 0) return false; { // truncate tail trash zeroes OutputMemoryFile file; file.Open(name); file.TruncateTailZero(); } // load file to memory InputMemoryFile file; if (!file.Open(name)) return false; size_t maxLen = std::numeric_limits::max(); const char* content = file.Read(maxLen); if (maxLen == 0) return false; QProtoParser parser; // extract commands from file content const char* const end = content + maxLen; while (content < end) { parser.Reset(); if (QParseResult::ok != parser.ParseRequest(content, end)) { ERR << "Load aof failed"; return false; } cmds_.push_back(parser.GetParams()); } return true; } } ================================================ FILE: QedisCore/QAOF.h ================================================ #ifndef BERT_QAOF_H #define BERT_QAOF_H #include #include #include "Log/MemoryFile.h" #include "AsyncBuffer.h" #include "QString.h" #include "QStore.h" namespace qedis { extern pid_t g_rewritePid; class QAOFThreadController { public: static QAOFThreadController& Instance(); QAOFThreadController(const QAOFThreadController& ) = delete; void operator= (const QAOFThreadController& ) = delete; void Start(); void Stop(); void Join(); void SaveCommand(const std::vector& params, int db); bool ProcessTmpBuffer(BufferSequence& bf); void SkipTmpBuffer(size_t n); static void RewriteDoneHandler(int exit, int signal); private: QAOFThreadController() : lastDb_(-1) {} class AOFThread { friend class QAOFThreadController; public: AOFThread() : alive_(false) { } ~AOFThread(); void SetAlive() { alive_ = true; } bool IsAlive() const { return alive_; } void Stop() { alive_ = false; } //void Close(); void SaveCommand(const std::vector& params); bool Flush(); void Run(); std::atomic alive_; OutputMemoryFile file_; AsyncBuffer buf_; std::promise pro_; // Effective modern C++ : Item 39 }; void _WriteSelectDB(int db, AsyncBuffer& dst); std::shared_ptr aofThread_; AsyncBuffer aofBuffer_; int lastDb_; }; class QAOFLoader { public: QAOFLoader(); bool Load(const char* name); const std::vector >& GetCmds() const { return cmds_; } private: std::vector > cmds_; }; template inline void WriteBulkString(const char* str, size_t strLen, DEST& dst) { char tmp[32]; size_t n = snprintf(tmp, sizeof tmp, "$%lu\r\n", strLen); dst.Write(tmp, n); dst.Write(str, strLen); dst.Write("\r\n", 2); } template inline void WriteBulkString(const QString& str, DEST& dst) { WriteBulkString(str.data(), str.size(), dst); } template inline void WriteMultiBulkLong(long val, DEST& dst) { char tmp[32]; size_t n = snprintf(tmp, sizeof tmp, "*%lu\r\n", val); dst.Write(tmp, n); } template inline void WriteBulkLong(long val, DEST& dst) { char tmp[32]; size_t n = snprintf(tmp, sizeof tmp, "%lu", val); WriteBulkString(tmp, n, dst); } template inline void SaveCommand(const std::vector& params, DEST& dst) { WriteMultiBulkLong(params.size(), dst); for (const auto& s : params) { WriteBulkString(s, dst); } } } #endif ================================================ FILE: QedisCore/QClient.cc ================================================ #include "Log/Logger.h" #include #include "QStore.h" #include "QCommand.h" #include "QConfig.h" #include "QSlowLog.h" #include "QClient.h" namespace qedis { QClient* QClient::s_current = 0; std::set, std::owner_less > > QClient::s_monitors; PacketLength QClient::_ProcessInlineCmd(const char* buf, size_t bytes, std::vector& params) { if (bytes < 2) return 0; QString res; for (size_t i = 0; i + 1 < bytes; ++ i) { if (buf[i] == '\r' && buf[i+1] == '\n') { if (!res.empty()) params.emplace_back(std::move(res)); return static_cast(i + 2); } if (isblank(buf[i])) { if (!res.empty()) { params.reserve(4); params.emplace_back(std::move(res)); } } else { res.reserve(16); res.push_back(buf[i]); } } return 0; } static int ProcessMaster(const char* start, const char* end) { auto state = QREPL.GetMasterState(); switch (state) { case QReplState_connected: // discard all requests before sync; // or continue serve with old data? TODO return static_cast(end - start); case QReplState_wait_auth: if (end - start >= 5) { if (strncasecmp(start, "+OK\r\n", 5) == 0) { QClient::Current()->SetAuth(); return 5; } else { assert (!!!"check masterauth config, master password maybe wrong"); } } else { return 0; } break; case QReplState_wait_replconf: if (end - start >= 5) { if (strncasecmp(start, "+OK\r\n", 5) == 0) { return 5; } else { assert (!!!"check error: send replconf command"); } } else { return 0; } break; case QReplState_wait_rdb: { const char* ptr = start; //recv RDB file if (QREPL.GetRdbSize() == std::size_t(-1)) { ++ ptr; // skip $ int s; if (QParseResult::ok == GetIntUntilCRLF(ptr, end - ptr, s)) { assert (s > 0); // check error for your masterauth or master config QREPL.SetRdbSize(s); USR << "recv rdb size " << s; } } else { std::size_t rdb = static_cast(end - ptr); QREPL.SaveTmpRdb(ptr, rdb); ptr += rdb; } return static_cast(ptr - start); } break; case QReplState_online: break; default: assert(!!!"wrong master state"); break; } return -1; // do nothing } PacketLength QClient::_HandlePacket(const char* start, std::size_t bytes) { s_current = this; const char* const end = start + bytes; const char* ptr = start; if (GetPeerAddr() == QREPL.GetMasterAddr()) { // check slave state auto recved = ProcessMaster(start, end); if (recved != -1) return static_cast(recved); } auto parseRet = parser_.ParseRequest(ptr, end); if (parseRet == QParseResult::error) { if (!parser_.IsInitialState()) { this->OnError(); return 0; } // try inline command std::vector params; auto len = _ProcessInlineCmd(ptr, bytes, params); if (len == 0) return 0; ptr += len; parser_.SetParams(params); parseRet = QParseResult::ok; } else if (parseRet != QParseResult::ok) { return static_cast(ptr - start); } QEDIS_DEFER { _Reset(); }; // handle packet const auto& params = parser_.GetParams(); if (params.empty()) return static_cast(ptr - start); QString cmd(params[0]); std::transform(params[0].begin(), params[0].end(), cmd.begin(), ::tolower); if (!auth_) { if (cmd == "auth") { auto now = ::time(nullptr); if (now <= lastauth_ + 1) { // avoid guess password. OnError(); return 0; } else { lastauth_ = now; } } else { ReplyError(QError_needAuth, &reply_); SendPacket(reply_); return static_cast(ptr - start); } } DBG << "client " << GetID() << ", cmd " << cmd; QSTORE.SelectDB(db_); FeedMonitors(params); const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd); if (!info) { ReplyError(QError_unknowCmd, &reply_); SendPacket(reply_); return static_cast(ptr - start); } // check transaction if (IsFlagOn(ClientFlag_multi)) { if (cmd != "multi" && cmd != "exec" && cmd != "watch" && cmd != "unwatch" && cmd != "discard") { if (!info->CheckParamsCount(static_cast(params.size()))) { ERR << "queue failed: cmd " << cmd.c_str() << " has params " << params.size(); ReplyError(info ? QError_param : QError_unknowCmd, &reply_); SendPacket(reply_); FlagExecWrong(); } else { if (!IsFlagOn(ClientFlag_wrongExec)) queueCmds_.push_back(params); SendPacket("+QUEUED\r\n", 9); INF << "queue cmd " << cmd.c_str(); } return static_cast(ptr - start); } } // check readonly slave and execute command QError err = QError_ok; if (QREPL.GetMasterState() != QReplState_none && !IsFlagOn(ClientFlag_master) && (info->attr & QCommandAttr::QAttr_write)) { ReplyError(err = QError_readonlySlave, &reply_); } else { QSlowLog::Instance().Begin(); err = QCommandTable::ExecuteCmd(params, info, IsFlagOn(ClientFlag_master) ? nullptr : &reply_); QSlowLog::Instance().EndAndStat(params); } SendPacket(reply_); if (err == QError_ok && (info->attr & QAttr_write)) { Propogate(params); } return static_cast(ptr - start); } QClient* QClient::Current() { return s_current; } QClient::QClient() : db_(0), flag_(0), name_("clientxxx") { auth_ = false; SelectDB(0); _Reset(); } void QClient::OnConnect() { const bool peerIsMaster = (GetPeerAddr() == QREPL.GetMasterAddr()); if (peerIsMaster) { QREPL.SetMasterState(QReplState_connected); QREPL.SetMaster(std::static_pointer_cast(shared_from_this())); SetName("MasterConnection"); SetFlag(ClientFlag_master); if (g_config.masterauth.empty()) SetAuth(); } else { if (g_config.password.empty()) SetAuth(); } } bool QClient::SelectDB(int db) { if (QSTORE.SelectDB(db) >= 0) { db_ = db; return true; } return false; } void QClient::_Reset() { s_current = 0; parser_.Reset(); reply_.Clear(); } bool QClient::Watch(int dbno, const QString& key) { DBG << "Client " << name_ << " watch " << key.c_str() << ", db " << dbno; return watchKeys_[dbno].insert(key).second; } bool QClient::NotifyDirty(int dbno, const QString& key) { if (IsFlagOn(ClientFlag_dirty)) { INF << "client is already dirty " << GetID(); return true; } if (watchKeys_[dbno].count(key)) { INF << GetID() << " client become dirty because key " << key << " in db " << dbno; SetFlag(ClientFlag_dirty); return true; } else { INF << "Dirty key is not exist: " << key << ", because client unwatch before dirty"; } return false; } bool QClient::Exec() { QEDIS_DEFER { this->ClearMulti(); this->ClearWatch(); }; if (IsFlagOn(ClientFlag_wrongExec)) { return false; } if (IsFlagOn(ClientFlag_dirty)) { FormatNullArray(&reply_); return true; } PreFormatMultiBulk(queueCmds_.size(), &reply_); for (const auto& cmd : queueCmds_) { DBG << "EXEC " << cmd[0] << ", for client " << GetID(); const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd[0]); QError err = QCommandTable::ExecuteCmd(cmd, info, &reply_); // may dirty clients; if (err == QError_ok && (info->attr & QAttr_write)) { Propogate(cmd); } } return true; } void QClient::ClearMulti() { queueCmds_.clear(); ClearFlag(ClientFlag_multi); ClearFlag(ClientFlag_wrongExec); } void QClient::ClearWatch() { watchKeys_.clear(); ClearFlag(ClientFlag_dirty); } bool QClient::WaitFor(const QString& key, const QString* target) { bool succ = waitingKeys_.insert(key).second; if (succ && target) { if (!target_.empty()) { ERR << "Wait failed for key " << key << ", because old target " << target_; waitingKeys_.erase(key); return false; } target_ = *target; } return succ; } void QClient::SetSlaveInfo() { slaveInfo_.reset(new QSlaveInfo()); } void QClient::AddCurrentToMonitor() { s_monitors.insert(std::static_pointer_cast(s_current->shared_from_this())); } void QClient::FeedMonitors(const std::vector& params) { assert(!params.empty()); if (s_monitors.empty()) return; char buf[512]; int n = snprintf(buf, sizeof buf, "+[db%d %s:%hu]: \"", QSTORE.GetDB(), s_current->peerAddr_.GetIP(), s_current->peerAddr_.GetPort()); assert(n > 0); for (const auto& e : params) { if (static_cast(sizeof buf) > n) n += snprintf(buf + n, sizeof buf - n, "%s ", e.data()); else break; } -- n; // no space follow last param for (auto it(s_monitors.begin()); it != s_monitors.end(); ) { auto m = it->lock(); if (m) { m->SendPacket(buf, n); m->SendPacket("\"" CRLF, 3); ++ it; } else { s_monitors.erase(it ++); } } } } ================================================ FILE: QedisCore/QClient.h ================================================ #ifndef BERT_QCLIENT_H #define BERT_QCLIENT_H #include "StreamSocket.h" #include "QCommon.h" #include "QReplication.h" #include "QProtoParser.h" #include #include #include namespace ConnectionTag { const int kQedisClient = 1; }; namespace qedis { enum ClientFlag { ClientFlag_multi = 0x1, ClientFlag_dirty = 0x1 << 1, ClientFlag_wrongExec = 0x1 << 2, ClientFlag_master = 0x1 << 3, }; class DB; struct QSlaveInfo; class QClient: public StreamSocket { private: PacketLength _HandlePacket(const char* msg, std::size_t len) override; public: QClient(); void OnConnect() override; bool SelectDB(int db); static QClient* Current(); //multi void SetFlag(unsigned flag) { flag_ |= flag; } void ClearFlag(unsigned flag) { flag_ &= ~flag; } bool IsFlagOn(unsigned flag) { return flag_ & flag; } void FlagExecWrong() { if (IsFlagOn(ClientFlag_multi)) SetFlag(ClientFlag_wrongExec); } bool Watch(int dbno, const QString& key); bool NotifyDirty(int dbno, const QString& key); bool Exec(); void ClearMulti(); void ClearWatch(); // pubsub std::size_t Subscribe(const QString& channel) { return channels_.insert(channel).second ? 1 : 0; } std::size_t UnSubscribe(const QString& channel) { return channels_.erase(channel); } std::size_t PSubscribe(const QString& channel) { return patternChannels_.insert(channel).second ? 1 : 0; } std::size_t PUnSubscribe(const QString& channel) { return patternChannels_.erase(channel); } const std::unordered_set& GetChannels() const { return channels_; } const std::unordered_set& GetPatternChannels() const { return patternChannels_; } std::size_t ChannelCount() const { return channels_.size(); } std::size_t PatternChannelCount() const { return patternChannels_.size(); } bool WaitFor(const QString& key, const QString* target = nullptr); const std::unordered_set WaitingKeys() const { return waitingKeys_; } void ClearWaitingKeys() { waitingKeys_.clear(), target_.clear(); } const QString& GetTarget() const { return target_; } void SetName(const QString& name) { name_ = name; } const QString& GetName() const { return name_; } void SetSlaveInfo(); QSlaveInfo* GetSlaveInfo() const { return slaveInfo_.get(); } static void AddCurrentToMonitor(); static void FeedMonitors(const std::vector& params); void SetAuth() { auth_ = true; } bool GetAuth() const { return auth_; } void RewriteCmd(std::vector& params) { parser_.SetParams(params); } private: PacketLength _ProcessInlineCmd(const char* , size_t, std::vector& ); void _Reset(); QProtoParser parser_; UnboundedBuffer reply_; int db_; std::unordered_set channels_; std::unordered_set patternChannels_; unsigned flag_; std::unordered_map > watchKeys_; std::vector > queueCmds_; // blocked list std::unordered_set waitingKeys_; QString target_; // slave info from master view std::unique_ptr slaveInfo_; // name std::string name_; // auth bool auth_; time_t lastauth_ = 0; static QClient* s_current; static std::set, std::owner_less > > s_monitors; }; } #endif ================================================ FILE: QedisCore/QCommand.cc ================================================ #include "QCommand.h" #include "QReplication.h" using std::size_t; namespace qedis { const QCommandInfo QCommandTable::s_info[] = { // key {"type", QAttr_read, 2, &type}, {"exists", QAttr_read, 2, &exists}, {"del", QAttr_write, -2, &del}, {"expire", QAttr_read, 3, &expire}, {"ttl", QAttr_read, 2, &ttl}, {"pexpire", QAttr_read, 3, &pexpire}, {"pttl", QAttr_read, 2, &pttl}, {"expireat", QAttr_read, 3, &expireat}, {"pexpireat", QAttr_read, 3, &pexpireat}, {"persist", QAttr_read, 2, &persist}, {"move", QAttr_write, 3, &move}, {"keys", QAttr_read, 2, &keys}, {"randomkey", QAttr_read, 1, &randomkey}, {"rename", QAttr_write, 3, &rename}, {"renamenx", QAttr_write, 3, &renamenx}, {"scan", QAttr_read, -2, &scan}, {"sort", QAttr_read, -2, &sort}, {"dump", QAttr_read, 2, &dump}, {"restore", QAttr_write, -4, &restore}, {"migrate", QAttr_read, -6, &migrate}, // server {"select", QAttr_read, 2, &select}, {"dbsize", QAttr_read, 1, &dbsize}, {"bgsave", QAttr_read, 1, &bgsave}, {"save", QAttr_read, 1, &save}, {"lastsave", QAttr_read, 1, &lastsave}, {"flushdb", QAttr_write, 1, &flushdb}, {"flushall", QAttr_write, 1, &flushall}, {"client", QAttr_read, -2, &client }, {"debug", QAttr_read, -2, &debug}, {"shutdown", QAttr_read, -1, &shutdown}, {"bgrewriteaof",QAttr_read, 1, &bgrewriteaof}, {"ping", QAttr_read, 1, &ping}, {"echo", QAttr_read, 2, &echo}, {"info", QAttr_read, -1, &info}, {"monitor", QAttr_read, 1, &monitor}, {"auth", QAttr_read, 2, &auth}, {"slowlog", QAttr_read, -2, &slowlog}, {"config", QAttr_read, -3, &config}, // string {"strlen", QAttr_read, 2, &strlen}, {"set", QAttr_write, 3, &set}, {"mset", QAttr_write, -3, &mset}, {"msetnx", QAttr_write, -3, &msetnx}, {"setnx", QAttr_write, 3, &setnx}, {"setex", QAttr_write, 4, &setex}, {"psetex", QAttr_write, 4, &psetex}, {"get", QAttr_read, 2, &get}, {"getset", QAttr_write, 3, &getset}, {"mget", QAttr_read, -2, &mget}, {"append", QAttr_write, 3, &append}, {"bitcount", QAttr_read, -2, &bitcount}, {"bitop", QAttr_write, -4, &bitop}, {"getbit", QAttr_read, 3, &getbit}, {"setbit", QAttr_write, 4, &setbit}, {"incr", QAttr_write, 2, &incr}, {"decr", QAttr_write, 2, &decr}, {"incrby", QAttr_write, 3, &incrby}, {"incrbyfloat", QAttr_write, 3, &incrbyfloat}, {"decrby", QAttr_write, 3, &decrby}, {"getrange", QAttr_read, 4, &getrange}, {"setrange", QAttr_write, 4, &setrange}, // list {"lpush", QAttr_write, -3, &lpush}, {"rpush", QAttr_write, -3, &rpush}, {"lpushx", QAttr_write, -3, &lpushx}, {"rpushx", QAttr_write, -3, &rpushx}, {"lpop", QAttr_write, 2, &lpop}, {"rpop", QAttr_write, 2, &rpop}, {"lindex", QAttr_read, 3, &lindex}, {"llen", QAttr_read, 2, &llen}, {"lset", QAttr_write, 4, &lset}, {"ltrim", QAttr_write, 4, <rim}, {"lrange", QAttr_read, 4, &lrange}, {"linsert", QAttr_write, 5, &linsert}, {"lrem", QAttr_write, 4, &lrem}, {"rpoplpush", QAttr_write, 3, &rpoplpush}, {"blpop", QAttr_write, -3, &blpop}, {"brpop", QAttr_write, -3, &brpop}, {"brpoplpush", QAttr_write, 4, &brpoplpush}, // hash {"hget", QAttr_read, 3, &hget}, {"hgetall", QAttr_read, 2, &hgetall}, {"hmget", QAttr_read, -3, &hmget}, {"hset", QAttr_write, 4, &hset}, {"hsetnx", QAttr_write, 4, &hsetnx}, {"hmset", QAttr_write, -4, &hmset}, {"hlen", QAttr_read, 2, &hlen}, {"hexists", QAttr_read, 3, &hexists}, {"hkeys", QAttr_read, 2, &hkeys}, {"hvals", QAttr_read, 2, &hvals}, {"hdel", QAttr_write, -3, &hdel}, {"hincrby", QAttr_write, 4, &hincrby}, {"hincrbyfloat",QAttr_write, 4, &hincrbyfloat}, {"hscan", QAttr_read, -3, &hscan}, {"hstrlen", QAttr_read, 3, &hstrlen}, // set {"sadd", QAttr_write, -3, &sadd}, {"scard", QAttr_read, 2, &scard}, {"sismember", QAttr_read, 3, &sismember}, {"srem", QAttr_write, -3, &srem}, {"smembers", QAttr_read, 2, &smembers}, {"sdiff", QAttr_read, -2, &sdiff}, {"sdiffstore", QAttr_write, -3, &sdiffstore}, {"sinter", QAttr_read, -2, &sinter}, {"sinterstore", QAttr_write, -3, &sinterstore}, {"sunion", QAttr_read, -2, &sunion}, {"sunionstore", QAttr_write, -3, &sunionstore}, {"smove", QAttr_write, 4, &smove}, {"spop", QAttr_write, 2, &spop}, {"srandmember", QAttr_read, 2, &srandmember}, {"sscan", QAttr_read, -3, &sscan}, // {"zadd", QAttr_write, -4, &zadd}, {"zcard", QAttr_read, 2, &zcard}, {"zrank", QAttr_read, 3, &zrank}, {"zrevrank", QAttr_read, 3, &zrevrank}, {"zrem", QAttr_write, -3, &zrem}, {"zincrby", QAttr_write, 4, &zincrby}, {"zscore", QAttr_read, 3, &zscore}, {"zrange", QAttr_read, -4, &zrange}, {"zrevrange", QAttr_read, -4, &zrevrange}, {"zrangebyscore", QAttr_read, -4, &zrangebyscore}, {"zrevrangebyscore",QAttr_read, -4, &zrevrangebyscore}, {"zremrangebyrank", QAttr_write, 4, &zremrangebyrank}, {"zremrangebyscore",QAttr_write, 4, &zremrangebyscore}, // pubsub {"subscribe", QAttr_read, -2, &subscribe}, {"unsubscribe", QAttr_read, -1, &unsubscribe}, {"publish", QAttr_read, 3, &publish}, {"psubscribe", QAttr_read, -2, &psubscribe}, {"punsubscribe",QAttr_read, -1, &punsubscribe}, {"pubsub", QAttr_read, -2, &pubsub}, // multi {"watch", QAttr_read, -2, &watch}, {"unwatch", QAttr_read, 1, &unwatch}, {"multi", QAttr_read, 1, &multi}, {"exec", QAttr_read, 1, &exec}, {"discard", QAttr_read, 1, &discard}, // replication {"sync", QAttr_read, 1, &sync}, {"psync", QAttr_read, 1, &sync}, {"slaveof", QAttr_read, 3, &slaveof}, {"replconf", QAttr_read, -3, &replconf}, // modules {"module", QAttr_read, -2, &module}, // help {"cmdlist", QAttr_read, 1, &cmdlist}, }; Delegate g_infoCollector; std::map QCommandTable::s_handlers; QCommandTable::QCommandTable() { Init(); } void QCommandTable::Init() { for (const auto& info : s_info) { s_handlers[info.cmd] = &info; } g_infoCollector += OnMemoryInfoCollect; g_infoCollector += OnServerInfoCollect; g_infoCollector += OnClientInfoCollect; g_infoCollector += std::bind(&QReplication::OnInfoCommand, &QREPL, std::placeholders::_1); } const QCommandInfo* QCommandTable::GetCommandInfo(const QString& cmd) { auto it(s_handlers.find(cmd)); if (it != s_handlers.end()) { return it->second; } return 0; } bool QCommandTable::AliasCommand(const std::map& aliases) { for (const auto& pair : aliases) { if (!AliasCommand(pair.first, pair.second)) return false; } return true; } bool QCommandTable::AliasCommand(const QString& oldKey, const QString& newKey) { auto info = DelCommand(oldKey); if (!info) return false; return AddCommand(newKey, info); } const QCommandInfo* QCommandTable::DelCommand(const QString& cmd) { auto it(s_handlers.find(cmd)); if (it != s_handlers.end()) { auto p = it->second; s_handlers.erase(it); return p; } return nullptr; } bool QCommandTable::AddCommand(const QString& cmd, const QCommandInfo* info) { if (cmd.empty() || cmd == "\"\"") return true; return s_handlers.insert(std::make_pair(cmd, info)).second; } QError QCommandTable::ExecuteCmd(const std::vector& params, const QCommandInfo* info, UnboundedBuffer* reply) { if (params.empty()) { ReplyError(QError_param, reply); return QError_param; } if (!info) { ReplyError(QError_unknowCmd, reply); return QError_unknowCmd; } if (!info->CheckParamsCount(static_cast(params.size()))) { ReplyError(QError_param, reply); return QError_param; } return info->handler(params, reply); } QError QCommandTable::ExecuteCmd(const std::vector& params, UnboundedBuffer* reply) { if (params.empty()) { ReplyError(QError_param, reply); return QError_param; } auto it(s_handlers.find(params[0])); if (it == s_handlers.end()) { ReplyError(QError_unknowCmd, reply); return QError_unknowCmd; } const QCommandInfo* info = it->second; if (!info->CheckParamsCount(static_cast(params.size()))) { ReplyError(QError_param, reply); return QError_param; } return info->handler(params, reply); } bool QCommandInfo::CheckParamsCount(int nParams) const { if (params > 0) return params == nParams; else return nParams + params >= 0; } QError cmdlist(const std::vector& params, UnboundedBuffer* reply) { PreFormatMultiBulk(QCommandTable::s_handlers.size(), reply); for (const auto& kv : QCommandTable::s_handlers) { FormatBulk(kv.first, reply); } return QError_ok; } } ================================================ FILE: QedisCore/QCommand.h ================================================ #ifndef BERT_QCOMMAND_H #define BERT_QCOMMAND_H #include #include #include "QCommon.h" #include "QString.h" #include "Delegate.h" namespace qedis { enum QCommandAttr { QAttr_read = 0x1, QAttr_write = 0x1 << 1, }; class UnboundedBuffer; using QCommandHandler = QError (const std::vector& params, UnboundedBuffer* reply); // key commands QCommandHandler type; QCommandHandler exists; QCommandHandler del; QCommandHandler expire; QCommandHandler pexpire; QCommandHandler expireat; QCommandHandler pexpireat; QCommandHandler ttl; QCommandHandler pttl; QCommandHandler persist; QCommandHandler move; QCommandHandler keys; QCommandHandler randomkey; QCommandHandler rename; QCommandHandler renamenx; QCommandHandler scan; QCommandHandler sort; QCommandHandler dump; QCommandHandler restore; QCommandHandler migrate; // server commands QCommandHandler select; QCommandHandler dbsize; QCommandHandler bgsave; QCommandHandler save; QCommandHandler lastsave; QCommandHandler flushdb; QCommandHandler flushall; QCommandHandler client; QCommandHandler debug; QCommandHandler shutdown; QCommandHandler bgrewriteaof; QCommandHandler ping; QCommandHandler echo; QCommandHandler info; QCommandHandler monitor; QCommandHandler auth; QCommandHandler slowlog; QCommandHandler config; // string commands QCommandHandler set; QCommandHandler get; QCommandHandler getrange; QCommandHandler setrange; QCommandHandler getset; QCommandHandler append; QCommandHandler bitcount; QCommandHandler bitop; QCommandHandler getbit; QCommandHandler setbit; QCommandHandler incr; QCommandHandler incrby; QCommandHandler incrbyfloat; QCommandHandler decr; QCommandHandler decrby; QCommandHandler mget; QCommandHandler mset; QCommandHandler msetnx; QCommandHandler setnx; QCommandHandler setex; QCommandHandler psetex; QCommandHandler strlen; // list commands QCommandHandler lpush; QCommandHandler rpush; QCommandHandler lpushx; QCommandHandler rpushx; QCommandHandler lpop; QCommandHandler rpop; QCommandHandler lindex; QCommandHandler llen; QCommandHandler lset; QCommandHandler ltrim; QCommandHandler lrange; QCommandHandler linsert; QCommandHandler lrem; QCommandHandler rpoplpush; QCommandHandler blpop; QCommandHandler brpop; QCommandHandler brpoplpush; // hash commands QCommandHandler hget; QCommandHandler hmget; QCommandHandler hgetall; QCommandHandler hset; QCommandHandler hsetnx; QCommandHandler hmset; QCommandHandler hlen; QCommandHandler hexists; QCommandHandler hkeys; QCommandHandler hvals; QCommandHandler hdel; QCommandHandler hincrby; QCommandHandler hincrbyfloat; QCommandHandler hscan; QCommandHandler hstrlen; // set commands QCommandHandler sadd; QCommandHandler scard; QCommandHandler srem; QCommandHandler sismember; QCommandHandler smembers; QCommandHandler sdiff; QCommandHandler sdiffstore; QCommandHandler sinter; QCommandHandler sinterstore; QCommandHandler sunion; QCommandHandler sunionstore; QCommandHandler smove; QCommandHandler spop; QCommandHandler srandmember; QCommandHandler sscan; // sset QCommandHandler zadd; QCommandHandler zcard; QCommandHandler zrank; QCommandHandler zrevrank; QCommandHandler zrem; QCommandHandler zincrby; QCommandHandler zscore; QCommandHandler zrange; QCommandHandler zrevrange; QCommandHandler zrangebyscore; QCommandHandler zrevrangebyscore; QCommandHandler zremrangebyrank; QCommandHandler zremrangebyscore; // pubsub QCommandHandler subscribe; QCommandHandler unsubscribe; QCommandHandler publish; QCommandHandler psubscribe; QCommandHandler punsubscribe; QCommandHandler pubsub; //multi QCommandHandler watch; QCommandHandler unwatch; QCommandHandler multi; QCommandHandler exec; QCommandHandler discard; // replication QCommandHandler sync; QCommandHandler slaveof; QCommandHandler replconf; // modules QCommandHandler module; // help QCommandHandler cmdlist; extern Delegate g_infoCollector; extern void OnMemoryInfoCollect(UnboundedBuffer& ); extern void OnServerInfoCollect(UnboundedBuffer& ); extern void OnClientInfoCollect(UnboundedBuffer& ); struct QCommandInfo { QString cmd; int attr; int params; QCommandHandler* handler; bool CheckParamsCount(int nParams) const; }; class QCommandTable { public: QCommandTable(); static void Init(); static const QCommandInfo* GetCommandInfo(const QString& cmd); static QError ExecuteCmd(const std::vector& params, const QCommandInfo* info, UnboundedBuffer* reply = nullptr); static QError ExecuteCmd(const std::vector& params, UnboundedBuffer* reply = nullptr); static bool AliasCommand(const std::map& aliases); static bool AliasCommand(const QString& oldKey, const QString& newKey); static bool AddCommand(const QString& cmd, const QCommandInfo* info); static const QCommandInfo* DelCommand(const QString& cmd); friend QCommandHandler cmdlist; private: static const QCommandInfo s_info[]; static std::map s_handlers; }; } #endif ================================================ FILE: QedisCore/QCommon.cc ================================================ #include "QCommon.h" #include "UnboundedBuffer.h" #include #include #include #include #include namespace qedis { struct QErrorInfo g_errorInfo[] = { {sizeof "+OK\r\n"- 1, "+OK\r\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"}, {sizeof "-ERR already exist"- 1, "-ERR already exist"}, {sizeof "-ERR no such key\r\n" - 1, "-ERR no such key\r\n"}, {sizeof "-ERR wrong number of arguments\r\n"- 1, "-ERR wrong number of arguments\r\n"}, {sizeof "-ERR Unknown command\r\n"- 1, "-ERR Unknown command\r\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"}, {sizeof "-ERR syntax error\r\n"-1, "-ERR syntax error\r\n"}, {sizeof "-EXECABORT Transaction discarded because of previous errors.\r\n"-1, "-EXECABORT Transaction discarded because of previous errors.\r\n"}, {sizeof "-WATCH inside MULTI is not allowed\r\n"-1, "-WATCH inside MULTI is not allowed\r\n"}, {sizeof "-EXEC without MULTI\r\n"-1, "-EXEC without MULTI\r\n"}, {sizeof "-ERR invalid DB index\r\n"-1, "-ERR invalid DB index\r\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"}, {sizeof "-ERR operation not permitted\r\n"-1, "-ERR operation not permitted\r\n"}, {sizeof "-ERR invalid password\r\n"-1, "-ERR invalid password\r\n"}, {sizeof "-ERR no such module\r\n"-1, "-ERR no such module\r\n"}, {sizeof "-ERR init module failed\r\n"-1, "-ERR init module failed\r\n"}, {sizeof "-ERR uninit module failed\r\n"-1, "-ERR uninit module failed\r\n"}, {sizeof "-ERR module already loaded\r\n"-1, "-ERR module already loaded\r\n"}, {sizeof "-BUSYKEY Target key name already exists.\r\n"-1, "-BUSYKEY Target key name already exists.\r\n"}, // }; int Double2Str(char* ptr, std::size_t nBytes, double val) { return snprintf(ptr, nBytes - 1, "%.6g", val); } bool TryStr2Long(const char* ptr, size_t nBytes, long& val) { bool negtive = false; size_t i = 0; if (ptr[0] == '-' || ptr[0] == '+') { if (nBytes <= 1 || !isdigit(ptr[1])) return false; negtive = (ptr[0] == '-'); i = 1; } val = 0; for (; i < nBytes; ++ i) { if (!isdigit(ptr[i])) break; if (!negtive && val > std::numeric_limits::max() / 10) { std::cerr << "long will overflow " << val << std::endl; return false; } if (negtive && val > (-(std::numeric_limits::min() + 1)) / 10) { std::cerr << "long will underflow " << val << std::endl; return false; } val *= 10; if (!negtive && val > std::numeric_limits::max() - ( ptr[i] - '0')) { std::cerr << "long will overflow " << val << std::endl; return false; } if (negtive && (val - 1) > (-(std::numeric_limits::min() + 1)) - (ptr[i] - '0')) { std::cerr << "long will underflow " << val << std::endl; return false; } val += ptr[i] - '0'; } if (negtive) { val *= -1; } return true; } bool Strtol(const char* ptr, size_t nBytes, long* outVal) { if (nBytes == 0 || nBytes > 20) // include the sign return false; errno = 0; char* pEnd = 0; *outVal = strtol(ptr, &pEnd, 0); if (errno == ERANGE || errno == EINVAL) return false; return pEnd == ptr + nBytes; } bool Strtoll(const char* ptr, size_t nBytes, long long* outVal) { if (nBytes == 0 || nBytes > 20) return false; errno = 0; char* pEnd = 0; *outVal = strtoll(ptr, &pEnd, 0); if (errno == ERANGE || errno == EINVAL) return false; return pEnd == ptr + nBytes; } bool Strtof(const char* ptr, size_t nBytes, float* outVal) { if (nBytes == 0 || nBytes > 20) return false; errno = 0; char* pEnd = 0; *outVal = strtof(ptr, &pEnd); if (errno == ERANGE || errno == EINVAL) return false; return pEnd == ptr + nBytes; } bool Strtod(const char* ptr, size_t nBytes, double* outVal) { if (nBytes == 0 || nBytes > 20) return false; errno = 0; char* pEnd = 0; *outVal = strtod(ptr, &pEnd); if (errno == ERANGE || errno == EINVAL) return false; return pEnd == ptr + nBytes; } const char* Strstr(const char* ptr, size_t nBytes, const char* pattern, size_t nBytes2) { if (!pattern || *pattern == 0) return 0; const char* ret = std::search(ptr, ptr + nBytes, pattern, pattern + nBytes2); return ret == ptr + nBytes ? 0 : ret; } const char* SearchCRLF(const char* ptr, size_t nBytes) { return Strstr(ptr, nBytes, CRLF, 2); } size_t FormatInt(long value, UnboundedBuffer* reply) { if (!reply) return 0; char val[32]; int len = snprintf(val, sizeof val, "%ld" CRLF, value); size_t oldSize = reply->ReadableSize(); reply->PushData(":", 1); reply->PushData(val, len); return reply->ReadableSize() - oldSize; } size_t FormatSingle(const char* str, size_t len, UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("+", 1); reply->PushData(str, len); reply->PushData(CRLF, 2); return reply->ReadableSize() - oldSize; } size_t FormatSingle(const QString& str, UnboundedBuffer* reply) { return FormatSingle(str.c_str(), str.size(), reply); } size_t FormatBulk(const char* str, size_t len, UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("$", 1); char val[32]; int tmp = snprintf(val, sizeof val - 1, "%lu" CRLF, len); reply->PushData(val, tmp); if (str && len > 0) { reply->PushData(str, len); } reply->PushData(CRLF, 2); return reply->ReadableSize() - oldSize; } size_t FormatBulk(const QString& str, UnboundedBuffer* reply) { return FormatBulk(str.c_str(), str.size(), reply); } size_t PreFormatMultiBulk(size_t nBulk, UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("*", 1); char val[32]; int tmp = snprintf(val, sizeof val - 1, "%lu" CRLF, nBulk); reply->PushData(val, tmp); return reply->ReadableSize() - oldSize; } std::size_t FormatMultiBulk(const std::vector vs, UnboundedBuffer* reply) { size_t size = 0; for (const auto& s : vs) size += FormatBulk(s, reply); return size; } std::size_t FormatEmptyBulk(UnboundedBuffer* reply) { return reply->PushData("$0" CRLF CRLF, 6); } void ReplyError(QError err, UnboundedBuffer* reply) { if (!reply) return; const QErrorInfo& info = g_errorInfo[err]; reply->PushData(info.errorStr, info.len); } size_t FormatNull(UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("$-1" CRLF, 5); return reply->ReadableSize() - oldSize; } size_t FormatNullArray(UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("*-1" CRLF, 5); return reply->ReadableSize() - oldSize; } size_t FormatOK(UnboundedBuffer* reply) { if (!reply) return 0; size_t oldSize = reply->ReadableSize(); reply->PushData("+OK" CRLF, 5); return reply->ReadableSize() - oldSize; } size_t Format1(UnboundedBuffer* reply) { if (!reply) return 0; const char* val = ":1\r\n"; size_t oldSize = reply->ReadableSize(); reply->PushData(val, 4); return reply->ReadableSize() - oldSize; } size_t Format0(UnboundedBuffer* reply) { if (!reply) return 0; const char* val = ":0\r\n"; size_t oldSize = reply->ReadableSize(); reply->PushData(val, 4); return reply->ReadableSize() - oldSize; } QParseResult GetIntUntilCRLF(const char*& ptr, std::size_t nBytes, int& val) { if (nBytes < 3) return QParseResult::wait; std::size_t i = 0; bool negtive = false; if (ptr[0] == '-') { negtive = true; ++ i; } else if (ptr[0] == '+') { ++ i; } int value = 0; for (; i < nBytes; ++ i) { if (isdigit(ptr[i])) { value *= 10; value += ptr[i] - '0'; } else { if (ptr[i] != '\r' || (i+1 < nBytes && ptr[i+1] != '\n')) return QParseResult::error; if (i + 1 == nBytes) return QParseResult::wait; break; } } if (negtive) value *= -1; ptr += i; ptr += 2; val = value; return QParseResult::ok; } std::vector SplitString(const QString& str, char seperator) { std::vector results; QString::size_type start = 0; QString::size_type sep = str.find(seperator); while (sep != QString::npos) { if (start < sep) results.emplace_back(str.substr(start, sep - start)); start = sep + 1; sep = str.find(seperator, start); } if (start != str.size()) results.emplace_back(str.substr(start)); return results; } } ================================================ FILE: QedisCore/QCommon.h ================================================ #ifndef BERT_QCOMMON_H #define BERT_QCOMMON_H #include #include #include #include #include #include #include "QString.h" #define QEDIS static_cast(Server::Instance()) #define CRLF "\r\n" namespace qedis { const int kStringMaxBytes = 1 * 1024 * 1024 * 1024; enum QType { QType_invalid, QType_string, QType_list, QType_set, QType_sortedSet, QType_hash, // < 16 }; enum QEncode { QEncode_invalid, QEncode_raw, // string QEncode_int, // string as int QEncode_list, QEncode_set, QEncode_hash, QEncode_sset, }; inline const char* EncodingStringInfo(unsigned encode) { switch (encode) { case QEncode_raw: return "raw"; case QEncode_int: return "int"; case QEncode_list: return "list"; case QEncode_set: return "set"; case QEncode_hash: return "hash"; case QEncode_sset: return "sset"; default: break; } return "unknown"; } enum QError { QError_nop = -1, QError_ok = 0, QError_type = 1, QError_exist = 2, QError_notExist = 3, QError_param = 4, QError_unknowCmd = 5, QError_nan = 6, QError_syntax = 7, QError_dirtyExec = 8, QError_watch = 9, QError_noMulti = 10, QError_invalidDB = 11, QError_readonlySlave = 12, QError_needAuth = 13, QError_errAuth = 14, QError_nomodule = 15, QError_moduleinit = 16, QError_moduleuninit = 17, QError_modulerepeat = 18, QError_busykey = 19, QError_max, }; extern struct QErrorInfo { int len; const char* errorStr; } g_errorInfo[] ; template inline std::size_t Number2Str(char* ptr, std::size_t nBytes, T val) { if (!ptr || nBytes < 2) return 0; if (val == 0) { ptr[0] = '0'; ptr[1] = 0; return 1; } bool negative = false; if (val < 0) { negative = true; val = -val; } std::size_t off = 0; while (val > 0) { if (off >= nBytes) return 0; ptr[off ++] = val % 10 + '0'; val /= 10; } if (negative) { if (off >= nBytes) return 0; ptr[off ++] = '-'; } std::reverse(ptr, ptr + off); ptr[off] = 0; return off; } int Double2Str(char* ptr, std::size_t nBytes, double val); bool TryStr2Long(const char* ptr, std::size_t nBytes, long& val); // only for decimal bool Strtol(const char* ptr, std::size_t nBytes, long* outVal); bool Strtoll(const char* ptr, std::size_t nBytes, long long* outVal); bool Strtof(const char* ptr, std::size_t nBytes, float* outVal); bool Strtod(const char* ptr, std::size_t nBytes, double* outVal); const char* Strstr(const char* ptr, std::size_t nBytes, const char* pattern, std::size_t nBytes2); const char* SearchCRLF(const char* ptr, std::size_t nBytes); class UnboundedBuffer; std::size_t FormatInt(long value, UnboundedBuffer* reply); std::size_t FormatSingle(const char* str, std::size_t len, UnboundedBuffer* reply); std::size_t FormatSingle(const QString& str, UnboundedBuffer* reply); std::size_t FormatBulk(const char* str, std::size_t len, UnboundedBuffer* reply); std::size_t FormatBulk(const QString& str, UnboundedBuffer* reply); std::size_t PreFormatMultiBulk(std::size_t nBulk, UnboundedBuffer* reply); std::size_t FormatMultiBulk(const std::vector vs, UnboundedBuffer* reply); std::size_t FormatEmptyBulk(UnboundedBuffer* reply); std::size_t FormatNull(UnboundedBuffer* reply); std::size_t FormatNullArray(UnboundedBuffer* reply); std::size_t FormatOK(UnboundedBuffer* reply); std::size_t Format1(UnboundedBuffer* reply); std::size_t Format0(UnboundedBuffer* reply); void ReplyError(QError err, UnboundedBuffer* reply); inline void AdjustIndex(long& start, long& end, size_t size) { if (size == 0) { end = 0, start = 1; return; } if (start < 0) start += size; if (start < 0) start = 0; if (end < 0) end += size; if (end >= static_cast(size)) end = size - 1; } struct NocaseComp { bool operator() (const QString& s1, const QString& s2) const { return strcasecmp(s1.c_str(), s2.c_str()) < 0; } bool operator() (const char* s1, const QString& s2) const { return strcasecmp(s1, s2.c_str()) < 0; } bool operator() (const QString& s1, const char* s2) const { return strcasecmp(s1.c_str(), s2) < 0; } }; enum class QParseResult : int8_t { ok, wait, error, }; QParseResult GetIntUntilCRLF(const char*& ptr, std::size_t nBytes, int& val); std::vector SplitString(const QString& str, char seperator); // Build redis request from multiple strings, use inline protocol template std::string BuildInlineRequest(Args&& ...); template inline std::string BuildInlineRequest(S&& s) { return std::string(std::forward(s)) + "\r\n"; } template inline std::string BuildInlineRequest(H&& head, T&&... tails) { std::string h(std::forward(head)); return h + " " + BuildInlineRequest(std::forward(tails)...); } // The defer class for C++11 class ExecuteOnScopeExit { public: ExecuteOnScopeExit() { } ExecuteOnScopeExit(ExecuteOnScopeExit&& e) { func_ = std::move(e.func_); } ExecuteOnScopeExit(const ExecuteOnScopeExit& e) = delete; void operator=(const ExecuteOnScopeExit& f) = delete; template ExecuteOnScopeExit(F&& f, Args&&... args) { auto temp = std::bind(std::forward(f), std::forward(args)...); func_ = [temp]() { (void)temp(); }; } ~ExecuteOnScopeExit() noexcept { if (func_) func_(); } private: std::function func_; }; #define CONCAT(a, b) a##b #define _MAKE_DEFER_HELPER_(line) qedis::ExecuteOnScopeExit CONCAT(defer, line) = [&]() #define QEDIS_DEFER _MAKE_DEFER_HELPER_(__LINE__) } #endif ================================================ FILE: QedisCore/QConfig.cc ================================================ #include #include #include "QConfig.h" #include "ConfigParser.h" namespace qedis { static void EraseQuotes(QString& str) { // convert "hello" to hello if (str.size() < 2) return; if (str[0] == '"' && str[str.size() - 1] == '"') { str.erase(str.begin()); str.pop_back(); } } extern std::vector SplitString(const QString& str, char seperator); QConfig g_config; QConfig::QConfig() { daemonize = false; pidfile = "/var/run/qedis.pid"; ip = "127.0.0.1"; port = 6379; timeout = 0; loglevel = "notice"; logdir = "stdout"; databases = 16; // rdb saveseconds = 999999999; savechanges = 999999999; rdbcompression = true; rdbchecksum = true; rdbfullname = "./dump.rdb"; maxclients = 10000; // aof appendonly = false; appendfilename = "appendonly.aof"; appendfsync = 0; // slow log slowlogtime = 0; slowlogmaxlen = 128; hz = 10; includefile = ""; maxmemory = 2 * 1024 * 1024 * 1024UL; maxmemorySamples = 5; noeviction = true; backend = BackEndNone; backendPath = "dump"; backendHz = 10; } bool LoadQedisConfig(const char* cfgFile, QConfig& cfg) { ConfigParser parser; if (!parser.Load(cfgFile)) return false; if (parser.GetData("daemonize") == "yes") cfg.daemonize = true; else cfg.daemonize = false; cfg.pidfile = parser.GetData("pidfile", cfg.pidfile); cfg.ip = parser.GetData("bind", cfg.ip); cfg.port = parser.GetData("port"); cfg.timeout = parser.GetData("timeout"); cfg.loglevel = parser.GetData("loglevel", cfg.loglevel); cfg.logdir = parser.GetData("logfile", cfg.logdir); EraseQuotes(cfg.logdir); if (cfg.logdir.empty()) cfg.logdir = "stdout"; cfg.databases = parser.GetData("databases", cfg.databases); cfg.password = parser.GetData("requirepass"); EraseQuotes(cfg.password); // alias command { std::vector alias(SplitString(parser.GetData("rename-command"), ' ')); if (alias.size() % 2 == 0) { for (auto it(alias.begin()); it != alias.end(); ) { const QString& oldCmd = *(it ++); const QString& newCmd = *(it ++); cfg.aliases[oldCmd] = newCmd; } } } // load rdb config std::vector saveInfo(SplitString(parser.GetData("save"), ' ')); if (!saveInfo.empty() && saveInfo.size() != 2) { EraseQuotes(saveInfo[0]); if (!(saveInfo.size() == 1 && saveInfo[0].empty())) { std::cerr << "bad format save rdb interval, bad string " << parser.GetData("save") << std::endl; return false; } } else if (!saveInfo.empty()) { cfg.saveseconds = std::stoi(saveInfo[0]); cfg.savechanges = std::stoi(saveInfo[1]); } if (cfg.saveseconds == 0) cfg.saveseconds = 999999999; if (cfg.savechanges == 0) cfg.savechanges = 999999999; cfg.rdbcompression = (parser.GetData("rdbcompression") == "yes"); cfg.rdbchecksum = (parser.GetData("rdbchecksum") == "yes"); cfg.rdbfullname = parser.GetData("dir", "./") + \ parser.GetData("dbfilename", "dump.rdb"); cfg.maxclients = parser.GetData("maxclients", 10000); cfg.appendonly = (parser.GetData("appendonly", "no") == "yes"); cfg.appendfilename = parser.GetData("appendfilename", "appendonly.aof"); if (cfg.appendfilename.size() <= 2) return false; if (cfg.appendfilename[0] == '"') // redis.conf use quote for string, but qedis do not. For compatiable... cfg.appendfilename = cfg.appendfilename.substr(1, cfg.appendfilename.size() - 2); QString tmpfsync = parser.GetData("appendfsync", "no"); // qedis always use "always", fsync is done in another thread if (tmpfsync == "everysec") { } else if (tmpfsync == "always") { } else { } cfg.slowlogtime = parser.GetData("slowlog-log-slower-than", 0); cfg.slowlogmaxlen = parser.GetData("slowlog-max-len", cfg.slowlogmaxlen); cfg.hz = parser.GetData("hz", 10); // load master ip port std::vector master(SplitString(parser.GetData("slaveof"), ' ')); if (master.size() == 2) { cfg.masterIp = std::move(master[0]); cfg.masterPort = static_cast(std::stoi(master[1])); } cfg.masterauth = parser.GetData("masterauth"); // load modules' names cfg.modules = parser.GetDataVector("loadmodule"); cfg.includefile = parser.GetData("include"); //TODO multi files include // lru cache cfg.maxmemory = parser.GetData("maxmemory", 2 * 1024 * 1024 * 1024UL); cfg.maxmemorySamples = parser.GetData("maxmemory-samples", 5); cfg.noeviction = (parser.GetData("maxmemory-policy", "noeviction") == "noeviction"); cfg.backend = parser.GetData("backend", BackEndNone); cfg.backendPath = parser.GetData("backendpath", cfg.backendPath); EraseQuotes(cfg.backendPath); cfg.backendHz = parser.GetData("backendhz", 10); // cluster cfg.enableCluster = parser.GetData("cluster", "off") == "on"; if (cfg.enableCluster) { cfg.centers = SplitString(parser.GetData("clustercenters"), ';'); cfg.setid = parser.GetData("setid", -1); } return cfg.CheckArgs(); } bool QConfig::CheckArgs() const { #define RETURN_IF_FAIL(cond)\ if (!(cond)) { \ std::cerr << #cond " failed\n"; \ return false; \ } RETURN_IF_FAIL(port > 0); RETURN_IF_FAIL(databases > 0); RETURN_IF_FAIL(maxclients > 0); RETURN_IF_FAIL(hz > 0 && hz < 500); RETURN_IF_FAIL(maxmemory >= 512 * 1024 * 1024UL); RETURN_IF_FAIL(maxmemorySamples > 0 && maxmemorySamples < 10); RETURN_IF_FAIL(backend >= BackEndNone && backend < BackEndMax); RETURN_IF_FAIL(backendHz >= 1 && backendHz <= 50); if (enableCluster) { RETURN_IF_FAIL(!centers.empty()); RETURN_IF_FAIL(setid >= 0); } #undef RETURN_IF_FAIL return true; } bool QConfig::CheckPassword(const QString& pwd) const { return password.empty() || password == pwd; } } ================================================ FILE: QedisCore/QConfig.h ================================================ #ifndef BERT_QCONFIG_H #define BERT_QCONFIG_H #include #include #include "QString.h" namespace qedis { enum BackEndType { BackEndNone = 0, BackEndLeveldb = 1, BackEndMax = 2, }; struct QConfig { bool daemonize; QString pidfile; QString ip; unsigned short port; int timeout; QString loglevel; QString logdir; // the log directory, differ from redis int databases; // auth QString password; std::map aliases; // @ rdb // save seconds changes int saveseconds; int savechanges; bool rdbcompression; // yes bool rdbchecksum; // yes QString rdbfullname; // ./dump.rdb int maxclients; // 10000 bool appendonly; // no QString appendfilename; // appendonly.aof int appendfsync; // no, everysec, always int slowlogtime; // 1000 microseconds int slowlogmaxlen; // 128 int hz; // 10 [1,500] QString masterIp; unsigned short masterPort; // replication QString masterauth; QString runid; QString includefile; // the template config std::vector modules; // modules // use redis as cache, level db as backup uint64_t maxmemory; // default 2GB int maxmemorySamples; // default 5 bool noeviction; // default true int backend; // enum BackEndType QString backendPath; int backendHz; // the frequency of dump to backend // cluster bool enableCluster = false; std::vector centers; int setid = -1; // sharding set id QConfig(); bool CheckArgs() const; bool CheckPassword(const QString& pwd) const; }; extern QConfig g_config; extern bool LoadQedisConfig(const char* cfgFile, QConfig& cfg); } #endif ================================================ FILE: QedisCore/QDB.cc ================================================ #include "QDB.h" #include "Log/Logger.h" #include #include #include #include extern "C" { #include "lzf/lzf.h" #include "redisZipList.h" #include "redisIntset.h" } extern "C" uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); namespace qedis { time_t g_lastQDBSave = 0; pid_t g_qdbPid = -1; // encoding static const int8_t kTypeString = 0; static const int8_t kTypeList = 1; static const int8_t kTypeSet = 2; static const int8_t kTypeZSet = 3; static const int8_t kTypeHash = 4; static const int8_t kTypeZipMap = 9; static const int8_t kTypeZipList=10; static const int8_t kTypeIntSet =11; static const int8_t kTypeZSetZipList = 12; static const int8_t kTypeHashZipList = 13; static const int8_t kTypeQuickList = 14; static const int8_t kQDBVersion = 6; static const int8_t kAux = 0xFA; static const int8_t kResizeDb = 0xFB; static const int8_t kExpireMs = 0xFC; static const int8_t kExpire = 0xFD; static const int8_t kSelectDB = 0xFE; static const int8_t kEOF = 0xFF; static const int8_t k6Bits = 0; static const int8_t k14bits = 1; static const int8_t k32bits = 2; static const int8_t kSpecial = 3; // the string may be interger, or lzf static const int8_t kLow6Bits = 0x3F; static const int8_t kEnc8Bits = 0; static const int8_t kEnc16Bits = 1; static const int8_t kEnc32Bits = 2; static const int8_t kEncLZF = 3; QDBSaver::QDBSaver(const char* qdbFile) { if (qdbFile && !qdb_.Open(qdbFile, false)) ERR << "QDBSaver can not open file " << qdbFile; } void QDBSaver::Save(const char* qdbFile) { char tmpFile[64] = ""; snprintf(tmpFile, sizeof tmpFile, "tmp_qdb_file_%d", getpid()); if (!qdb_.Open(tmpFile, false)) assert (false); char buf[16]; snprintf(buf, sizeof buf, "REDIS%04d", kQDBVersion); qdb_.Write(buf, 9); for (int dbno = 0; true; ++ dbno) { if (QSTORE.SelectDB(dbno) == -1) break; if (QSTORE.DBSize() == 0) continue; // But redis will save empty db qdb_.Write(&kSelectDB, 1); SaveLength(dbno); uint64_t now = ::Now(); for (const auto& kv : QSTORE) { int64_t ttl = QSTORE.TTL(kv.first, now); if (ttl > 0) { ttl += now; qdb_.Write(&kExpireMs, 1); qdb_.Write(&ttl, sizeof ttl); } else if (ttl == QStore::ExpireResult::expired) { continue; } SaveType(kv.second); SaveKey(kv.first); SaveObject(kv.second); } } qdb_.Write(&kEOF, 1); // crc 8 bytes InputMemoryFile file; file.Open(tmpFile); auto len = qdb_.Offset(); auto data = file.Read(len); const uint64_t crc = crc64(0, (const unsigned char* )data, len); qdb_.Write(&crc, sizeof crc); if (::rename(tmpFile, qdbFile) != 0) { perror("rename error"); assert (false); } } void QDBSaver::SaveType(const QObject& obj) { switch (obj.encoding) { case QEncode_raw: case QEncode_int: qdb_.Write(&kTypeString, 1); break; case QEncode_list: qdb_.Write(&kTypeList, 1); break; case QEncode_hash: qdb_.Write(&kTypeHash, 1); break; case QEncode_set: qdb_.Write(&kTypeSet, 1); break; case QEncode_sset: qdb_.Write(&kTypeZSet, 1); break; default: assert(!!!"Wrong encoding"); break; } } void QDBSaver::SaveKey(const QString& key) { SaveString(key); } void QDBSaver::SaveObject(const QObject& obj) { switch (obj.encoding) { case QEncode_raw: case QEncode_int: SaveString(*GetDecodedString(&obj)); break; case QEncode_list: _SaveList(obj.CastList()); break; case QEncode_set: _SaveSet(obj.CastSet()); break; case QEncode_hash: _SaveHash(obj.CastHash()); break; case QEncode_sset: _SaveSSet(obj.CastSortedSet()); break; default: break; } } /* Copy from redis~ * Save a double value. Doubles are saved as strings prefixed by an unsigned * 8 bit integer specifying the length of the representation. * This 8 bit integer has special values in order to specify the following * conditions: * 253: not a number * 254: + inf * 255: - inf */ void QDBSaver::_SaveDoubleValue(double val) { unsigned char buf[128]; int len; if (isnan(val)) { buf[0] = 253; len = 1; } else if (!std::isfinite(val)) { len = 1; buf[0] = (val < 0) ? 255 : 254; } else { snprintf((char*)buf+1,sizeof(buf)-1,"%.6g",val); buf[0] = strlen((char*)buf+1); len = buf[0]+1; } qdb_.Write(buf,len); } void QDBSaver::_SaveList(const PLIST& l) { SaveLength(l->size()); for (const auto& e : *l) { SaveString(e); } } void QDBSaver::_SaveSet(const PSET& s) { SaveLength(s->size()); for (const auto& e : *s) { SaveString(e); } } void QDBSaver::_SaveHash(const PHASH& h) { SaveLength(h->size()); for (const auto& e : *h) { SaveString(e.first); SaveString(e.second); } } void QDBSaver::_SaveSSet(const PSSET& ss) { SaveLength(ss->Size()); for (const auto& e : *ss) { SaveString(e.first); _SaveDoubleValue(e.second); } } void QDBSaver::SaveString(const QString& str) { if (str.size() < 10) { long lVal; if (Strtol(str.data(), str.size(), &lVal)) { SaveString(lVal); return; } } if (!SaveLZFString(str)) { SaveLength(str.size()); qdb_.Write(str.data(), str.size()); } } void QDBSaver::SaveLength(uint64_t len) { assert ((len & ~0xFFFFFFFF) == 0); if (len < (1 << 6)) { len &= kLow6Bits; len |= k6Bits << 6; qdb_.Write(&len, 1); } else if (len < (1 << 14)) { uint16_t encodeLen = (len >> 8) & kLow6Bits; encodeLen |= k14bits << 6; encodeLen |= (len & 0xFF) << 8; qdb_.Write(&encodeLen, 2); } else { int8_t encFlag = static_cast(k32bits << 6); qdb_.Write(&encFlag, 1); len = htonl(len); qdb_.Write(&len, 4); } } void QDBSaver::SaveString(int64_t intVal) { uint8_t specialByte = kSpecial << 6; if ((intVal & ~0x7F) == 0) { specialByte |= kEnc8Bits; qdb_.Write(&specialByte, 1); qdb_.Write(&intVal, 1); } else if ((intVal & ~0x7FFF) == 0) { specialByte |= kEnc16Bits; qdb_.Write(&specialByte, 1); qdb_.Write(&intVal, 2); } else if ((intVal & ~0x7FFFFFFF) == 0) { specialByte |= kEnc32Bits; qdb_.Write(&specialByte, 1); qdb_.Write(&intVal, 4); } else { char buf[64]; auto len = Number2Str(buf, sizeof buf, intVal); SaveLength(static_cast(len)); qdb_.Write(buf, len); } } bool QDBSaver::SaveLZFString(const QString& str) { if (str.size() < 20) return false; unsigned outlen = static_cast(str.size() - 4); std::unique_ptr outBuf(new char[outlen + 1]); auto compressLen = lzf_compress((const void*)str.data(), static_cast(str.size()), outBuf.get(), outlen); if (compressLen == 0) { ERR << "compress len = 0"; return false; } int8_t specialByte = static_cast(kSpecial << 6) | kEncLZF; qdb_.Write(&specialByte, 1); // compress len + raw len + str data; SaveLength(compressLen); SaveLength(str.size()); qdb_.Write(outBuf.get(), compressLen); DBG << "compress len " << compressLen << ", raw len " << str.size(); return true; } void QDBSaver::SaveDoneHandler(int exitRet, int whatSignal) { if (exitRet == 0 && whatSignal == 0) { INF << "save rdb success"; g_lastQDBSave = time(NULL); QStore::dirty_ = 0; } else { ERR << "save rdb failed with exit result " << exitRet << ", signal " << whatSignal; } g_qdbPid = -1; } QDBLoader::QDBLoader(const char *data, size_t len) { if (data && len) qdb_.Attach(data, len); } int QDBLoader::Load(const char *filename) { if (!qdb_.Open(filename)) { return - __LINE__; } // check the magic string "REDIS" and version number size_t len = 9; const char* data = qdb_.Read(len); if (len != 9) { return - __LINE__; } long qdbversion; if (!Strtol(data + 5, 4, &qdbversion) || qdbversion < 6) { return - __LINE__; } qdb_.Skip(9); // SELECTDB + dbno // type1 + key + obj // EOF + crc int64_t absTimeout = 0; int8_t indicator = 0; bool eof = false; while (!eof) { try { indicator = LoadByte(); } catch (const std::runtime_error& e) { ERR << "LoadByte with exception: " << e.what(); return - __LINE__; } switch (indicator) { case kEOF: DBG << "encounter EOF"; eof = true; break; case kAux: DBG << "encounter AUX"; try { _LoadAux(); } catch (const std::runtime_error& e) { ERR << "_LoadAux with exception: " << e.what(); return - __LINE__; } break; case kResizeDb: DBG << "encounter kResizeDb"; try { _LoadResizeDB(); } catch (const std::runtime_error& e) { ERR << "_LoadResizeDB with exception: " << e.what(); return - __LINE__; } break; case kSelectDB: { bool special; size_t dbno = 0; try { dbno = LoadLength(special); } catch (const std::runtime_error& e) { ERR << "LoadLength with exception: " << e.what(); return - __LINE__; } if (special) { ERR << "LoadLength should not be special"; return - __LINE__; } // check db no if (dbno > kMaxDbNum) { ERR << "Abnormal db number " << dbno; return __LINE__; } if (QSTORE.SelectDB(static_cast(dbno)) == -1) { ERR << "DB NUMBER is differ from RDB file"; return __LINE__; } DBG << "encounter Select DB " << dbno; break; } case kExpireMs: try { absTimeout = qdb_.Read(); } catch (const std::runtime_error& e) { ERR << "Read kExpireMs with exception: " << e.what(); return - __LINE__; } break; case kExpire: try { absTimeout = qdb_.Read(); absTimeout *= 1000; } catch (const std::runtime_error& e) { ERR << "Read kExpire with exception: " << e.what(); return - __LINE__; } break; case kTypeString: case kTypeList: case kTypeZipList: case kTypeSet: case kTypeIntSet: case kTypeHash: case kTypeHashZipList: case kTypeZipMap: case kTypeZSet: case kTypeZSetZipList: case kTypeQuickList: { QString key; QObject obj; try { key = LoadKey(); obj = LoadObject(indicator); assert (obj.type != QType_invalid); DBG << "encounter key = " << key << ", obj.encoding = " << obj.encoding; } catch (const std::runtime_error& e) { ERR << "Read object with exception: " << e.what(); return - __LINE__; } if (absTimeout < 0) { ERR << "Wrong absTimeout " << absTimeout; return - __LINE__; } if (absTimeout == 0) { QSTORE.SetValue(key, std::move(obj)); } else if (absTimeout > 0) { if (absTimeout > static_cast(::Now())) { DBG << key << " load timeout " << absTimeout; QSTORE.SetValue(key, std::move(obj)); QSTORE.SetExpire(key, absTimeout); } else { INF << key << " is already time out"; } absTimeout = 0; } break; } default: ERR << indicator << " is unknown type"; return - __LINE__; } } return 0; } int8_t QDBLoader::LoadByte() { return qdb_.Read(); } size_t QDBLoader::LoadLength(bool& special) { const int8_t byte = qdb_.Read(); special = false; size_t lenResult = 0; switch ((byte & 0xC0) >> 6) { case k6Bits: { lenResult = byte & kLow6Bits; break; } case k14bits: { lenResult = byte & kLow6Bits; // high 6 bits; lenResult <<= 8; const uint8_t bytelow = qdb_.Read(); lenResult |= bytelow; break; } case k32bits: { const int32_t fourbytes = qdb_.Read(); lenResult = ntohl(fourbytes); break; } case kSpecial: { special = true; lenResult = byte & kLow6Bits; break; } default: { std::string s("Wrong length type:" + std::to_string(byte)); throw std::runtime_error(std::move(s)); } } return lenResult; } QObject QDBLoader::LoadSpecialStringObject(size_t specialVal) { bool isInt = true; long val; switch (specialVal) { case kEnc8Bits: { val = qdb_.Read(); break; } case kEnc16Bits: { val = qdb_.Read(); break; } case kEnc32Bits: { val = qdb_.Read(); break; } case kEncLZF: { isInt = false; break; } default: throw std::runtime_error("Wrong specialVal"); } if (isInt) return QObject::CreateString(val); else return QObject::CreateString(LoadLZFString()); } QString QDBLoader::LoadString(size_t strLen) { const char* str = qdb_.Read(strLen); qdb_.Skip(strLen); return QString(str, strLen); } QString QDBLoader::LoadLZFString() { bool special; size_t compressLen = LoadLength(special); if (special) { ERR << "Should not be special for compressLen"; return QString(); } unsigned rawLen = static_cast(LoadLength(special)); if (special) { ERR << "Should not be special for rawLen"; return QString(); } const char* compressStr = qdb_.Read(compressLen); QString val; val.resize(rawLen); if (lzf_decompress(compressStr, static_cast(compressLen), &val[0], rawLen) == 0) { ERR << "decompress error"; return QString(); } qdb_.Skip(compressLen); return val; } QString QDBLoader::LoadKey() { return _LoadGenericString(); } QObject QDBLoader::LoadObject(int8_t type) { switch (type) { case kTypeString: { bool special; size_t len = LoadLength(special); if (special) { return LoadSpecialStringObject(len); } else { return QObject::CreateString(LoadString(len)); } } case kTypeList: { return _LoadList(); } case kTypeZipList: { return _LoadZipList(kTypeZipList); } case kTypeSet: { return _LoadSet(); } case kTypeIntSet: { return _LoadIntset(); } case kTypeHash: { return _LoadHash(); } case kTypeHashZipList: { return _LoadZipList(kTypeHashZipList); } case kTypeZipMap: { assert(!!!"zipmap should be replaced with ziplist"); break; } case kTypeZSet: { return _LoadSSet(); } case kTypeZSetZipList: { return _LoadZipList(kTypeZSetZipList); } case kTypeQuickList: { return _LoadQuickList(); } default: break; } return QObject(QType_invalid); } QString QDBLoader::_LoadGenericString() { bool special; size_t len = LoadLength(special); if (special) { QObject obj = LoadSpecialStringObject(len); return *GetDecodedString(&obj); } else { return LoadString(len); } } QObject QDBLoader::_LoadList() { bool special; const auto len = LoadLength(special); if (special) throw std::runtime_error("LoadList length should not be special"); DBG << "list length = " << len; QObject obj(QObject::CreateList()); PLIST list(obj.CastList()); for (size_t i = 0; i < len; ++ i) { const auto elemLen = LoadLength(special); QString elem; if (special) { QObject str = LoadSpecialStringObject(elemLen); elem = *GetDecodedString(&str); } else { elem = LoadString(elemLen); } list->push_back(elem); DBG << "list elem : " << elem.c_str(); } return obj; } QObject QDBLoader::_LoadSet() { bool special; const auto len = LoadLength(special); if (special) throw std::runtime_error("LoadSet length should not be special"); DBG << "set length = " << len; QObject obj(QObject::CreateSet()); PSET set(obj.CastSet()); for (size_t i = 0; i < len; ++ i) { const auto elemLen = LoadLength(special); QString elem; if (special) { QObject str = LoadSpecialStringObject(elemLen); elem = *GetDecodedString(&str); } else { elem = LoadString(elemLen); } set->insert(elem); DBG << "set elem : " << elem.c_str(); } return obj; } QObject QDBLoader::_LoadHash() { bool special; const auto len = LoadLength(special); if (special) throw std::runtime_error("LoadHash length should not be special"); DBG << "hash length = " << len; QObject obj(QObject::CreateHash()); PHASH hash(obj.CastHash()); for (size_t i = 0; i < len; ++ i) { const auto keyLen = LoadLength(special); QString key; if (special) { QObject str = LoadSpecialStringObject(keyLen); key = *GetDecodedString(&str); } else { key = LoadString(keyLen); } const auto valLen = LoadLength(special); QString val; if (special) { QObject str = LoadSpecialStringObject(valLen); val = *GetDecodedString(&str); } else { val = LoadString(valLen); } hash->insert(QHash::value_type(key, val)); DBG << "hash key : " << key.c_str() << " val : " << val.c_str(); } return obj; } QObject QDBLoader::_LoadSSet() { bool special; const auto len = LoadLength(special); if (special) throw std::runtime_error("LoadSortedSet length should not be special"); DBG << "sset length = " << len; QObject obj(QObject::CreateSSet()); PSSET sset(obj.CastSortedSet()); for (size_t i = 0; i < len; ++ i) { const auto memberLen = LoadLength(special); QString member; if (special) { QObject str = LoadSpecialStringObject(memberLen); member = *GetDecodedString(&str); } else { member = LoadString(memberLen); } const auto score = _LoadDoubleValue(); sset->AddMember(member, static_cast(score)); DBG << "sset member : " << member.c_str() << " score : " << score; } return obj; } double QDBLoader::_LoadDoubleValue() { const uint8_t byte1st = qdb_.Read(); double dvalue; switch (byte1st) { case 253: { dvalue = NAN; break; } case 254: { dvalue = INFINITY; break; } case 255: { dvalue = INFINITY; break; } default: { size_t len = byte1st; const char* val = qdb_.Read(len); assert(len == byte1st); qdb_.Skip(len); std::istringstream is(std::string(val, len)); is >> dvalue; break; } } DBG << "load double value " << dvalue; return dvalue; } struct ZipListElement { unsigned char* sval; unsigned int slen; long long lval; QString ToString() const { if (sval) { const QString str((const char* )sval, QString::size_type(slen)); DBG << "string zip list element " << str; return str; } else return LongToString(); } QString LongToString() const { assert(!sval); QString str(16, 0); auto len = Number2Str(&str[0], 16, lval); str.resize(len); DBG << "long zip list element " << str; return str; } }; QObject QDBLoader::_LoadZipList(int8_t type) { QString zl = _LoadGenericString(); return _LoadZipList(zl, type); } QObject QDBLoader::_LoadZipList(const QString& zl, int8_t type) { unsigned char* zlist = (unsigned char* )&zl[0]; unsigned nElem = ziplistLen(zlist); std::vector elements; elements.resize(nElem); for (unsigned i = 0; i < nElem; ++ i) { unsigned char* elem = ziplistIndex(zlist, (int)i); int succ = ziplistGet(elem, &elements[i].sval, &elements[i].slen, &elements[i].lval); assert (succ); } switch (type) { case kTypeZipList: { QObject obj(QObject::CreateList()); PLIST list(obj.CastList()); for (const auto& elem : elements) { list->push_back(elem.ToString()); } return obj; } case kTypeHashZipList: { QObject obj(QObject::CreateHash()); PHASH hash(obj.CastHash()); assert(elements.size() % 2 == 0); for (auto it(elements.begin()); it != elements.end(); ++ it) { auto key = it; auto value = ++ it; hash->insert(QHash::value_type(key->ToString(), value->ToString())); } return obj; } case kTypeZSetZipList: { QObject obj(QObject::CreateSSet()); PSSET sset(obj.CastSortedSet()); assert(elements.size() % 2 == 0); for (auto it(elements.begin()); it != elements.end(); ++ it) { const QString& member = it->ToString(); ++ it; double score; if (it->sval) { Strtod((const char* )it->sval, it->slen, &score); } else { score = it->lval; } DBG << "sset member " << member << ", score " << score; sset->AddMember(member, score); } return obj; } default: assert(!!!"illegal data type"); break; } return QObject(QType_invalid); } QObject QDBLoader::_LoadIntset() { QString str = _LoadGenericString(); intset* iset = (intset* )&str[0]; unsigned nElem = intsetLen(iset); std::vector elements; elements.resize(nElem); for (unsigned i = 0; i < nElem; ++ i) { intsetGet(iset, i, &elements[i]); } QObject obj(QObject::CreateSet()); PSET set(obj.CastSet()); for (auto v : elements) { char buf[64]; auto bytes = Number2Str(buf, sizeof buf, v); set->insert(QString(buf, bytes)); } return obj; } QObject QDBLoader::_LoadQuickList() { bool special = true; auto nElem = LoadLength(special); QObject obj(QObject::CreateList()); PLIST list(obj.CastList()); while (nElem -- > 0) { QString zl = _LoadGenericString(); if (zl.empty()) continue; QObject l = _LoadZipList(zl, kTypeZipList); PLIST tmplist(l.CastList()); if (!tmplist->empty()) list->splice(list->end(), *tmplist); } return obj; } void QDBLoader::_LoadAux() { /* AUX: generic string-string fields. Use to add state to RDB * which is backward compatible. Implementations of RDB loading * are required to skip AUX fields they don't understand. * * An AUX field is composed of two strings: key and value. */ QString auxkey = _LoadGenericString(); QString auxvalue = _LoadGenericString(); if (!auxkey.empty() && auxkey[0] == '%') { /* All the fields with a name staring with '%' are considered * information fields and are logged at startup with a log * level of NOTICE. */ USR << "RDB '" << auxkey << "': " << auxvalue; } else { /* We ignore fields we don't understand, as by AUX field * contract. */ DBG << "Unrecognized RDB AUX field: '" << auxkey << "': " << auxvalue; } } void QDBLoader::_LoadResizeDB() { bool special = true; auto dbsize = LoadLength(special); if (special) throw std::runtime_error("Should not be special when LoadLength"); auto expiresize = LoadLength(special); if (special) throw std::runtime_error("Should not be special when LoadLength"); // Qedis just ignore this (void)special; (void)dbsize; (void)expiresize; } std::string DumpObject(const QObject& val) { std::string file = "qedisdump"; file += std::to_string(getpid()); { QDBSaver saver(file.data()); saver.SaveType(val); saver.SaveObject(val); } // 2 bytes version char v[2]; v[0] = kQDBVersion & 0xFF; v[1] = (kQDBVersion >> 8) & 0xFF; InputMemoryFile ifile; ifile.Open(file.data()); std::size_t size = std::numeric_limits::max(); const char* data = ifile.Read(size); std::string result(data, size); result.append(v, 2); // 8 bytes crc const uint64_t crc = crc64(0, (const unsigned char* )result.data(), result.size()); result.append((const char*)&crc, 8); unlink(file.data()); return result; } QObject RestoreObject(const char* data, size_t len) { try { QDBLoader loader(data, len); int8_t type = loader.LoadByte(); QObject obj = loader.LoadObject(type); // check version char v[2]; v[0] = loader.LoadByte(); v[1] = loader.LoadByte(); if (v[0] != kQDBVersion) return QObject(); // check crc uint64_t crc = 0; unsigned char* p = (unsigned char*)&crc; for (size_t i = 0; i < sizeof(crc); ++ i) { p[i] = loader.LoadByte(); } const uint64_t expectCrc = crc64(0, (const unsigned char* )data, len - 8); if (expectCrc != crc) return QObject(); return obj; } catch (const std::runtime_error& e) { ERR << "RestoreObject with exception: " << e.what(); return QObject(); } } QError dump(const std::vector& params, UnboundedBuffer* reply) { QObject* val; if (QSTORE.GetValue(params[1], val) != QError_ok) { ReplyError(QError_notExist, reply); return QError_notExist; } std::string str(DumpObject(*val)); FormatBulk(str, reply); return QError_ok; } // restore key ttl ser-val replace QError restore(const std::vector& params, UnboundedBuffer* reply) { QObject obj(RestoreObject(params[3].data(), params[3].size())); if (obj.type == QType_invalid) { char err[] = "-ERR DUMP payload version or checksum are wrong\r\n"; if (reply) reply->PushData(err, sizeof err - 1); return QError_nop; } long ttl = 0; if (!Strtol(params[2].c_str(), params[2].size(), &ttl)) { ReplyError(QError_nan, reply); return QError_nan; } bool replace = false; if (params.size() == 5 && params[4] == "replace") replace = true; const auto& key = params[1]; if (!replace && QSTORE.GetObject(key)) { USR << "Can not replace " << key; ReplyError(QError_busykey, reply); return QError_busykey; } QSTORE.SetValue(key, std::move(obj)); if (ttl > 0) QSTORE.SetExpireAfter(key, ttl); FormatOK(reply); return QError_ok; } } // end namespace qedis ================================================ FILE: QedisCore/QDB.h ================================================ #ifndef BERT_QDB_H #define BERT_QDB_H #include "Log/MemoryFile.h" #include "QStore.h" namespace qedis { class QDBSaver { public: explicit QDBSaver(const char* file = nullptr); void Save(const char* qdbFile); void SaveType(const QObject& obj); void SaveKey(const QString& key); void SaveObject(const QObject& obj); void SaveString(const QString& str); void SaveLength(uint64_t len); // big endian void SaveString(int64_t intVal); bool SaveLZFString(const QString& str); static void SaveDoneHandler(int exit, int signal); private: void _SaveDoubleValue(double val); void _SaveList(const PLIST& l); void _SaveSet(const PSET& s); void _SaveHash(const PHASH& h); void _SaveSSet(const PSSET& ss); OutputMemoryFile qdb_; }; extern time_t g_lastQDBSave; extern pid_t g_qdbPid; class QDBLoader { public: explicit QDBLoader(const char* data = nullptr, size_t len = 0); int Load(const char* filename); int8_t LoadByte(); size_t LoadLength(bool& special); QObject LoadSpecialStringObject(size_t specialVal); QString LoadString(size_t strLen); QString LoadLZFString(); QString LoadKey(); QObject LoadObject(int8_t type); private: QString _LoadGenericString(); QObject _LoadList(); QObject _LoadSet(); QObject _LoadHash(); QObject _LoadSSet(); double _LoadDoubleValue(); QObject _LoadZipList(int8_t type); QObject _LoadZipList(const QString& zl, int8_t type); QObject _LoadIntset(); QObject _LoadQuickList(); void _LoadAux(); void _LoadResizeDB(); InputMemoryFile qdb_; }; std::string DumpObject(const QObject& val); QObject RestoreObject(const char* data, size_t len); } #endif ================================================ FILE: QedisCore/QDumpInterface.h ================================================ #ifndef BERT_QDUMPINTERFACE_H #define BERT_QDUMPINTERFACE_H #include #include "QString.h" namespace qedis { struct QObject; class QDumpInterface { public: virtual ~QDumpInterface() {} virtual QObject Get(const QString& key) = 0; virtual bool Put(const QString& key, const QObject& obj, int64_t ttl = 0) = 0; virtual bool Put(const QString& key) = 0; virtual bool Delete(const QString& key) = 0; //std::vector MultiGet(const QString& key); //bool MultiPut(const QString& key, const QObject& obj, int64_t ttl = 0); //SaveAllRedisTolevelDb(); //LoadAllLeveldbToRedis(); }; } #endif ================================================ FILE: QedisCore/QGlobRegex.cc ================================================ #include #include #include "QGlobRegex.h" namespace qedis { bool NotGlobRegex(const char* pattern, std::size_t plen) { for (std::size_t i(0); i < plen; ++ i) { if (pattern[i] == '?' || pattern[i] == '\\' || pattern[i] == '[' || pattern[i] == ']' || pattern[i] == '*' || pattern[i] == '^' || pattern[i] == '-') return false; // may be regex, may not, who cares? } return true; // must not be regex } QGlobRegex::QGlobRegex(const char* pattern, std::size_t plen, const char* text, std::size_t tlen) { SetPattern(pattern, plen); SetText(text, tlen); } void QGlobRegex::SetPattern(const char* pattern, std::size_t plen) { pattern_ = pattern; pLen_ = plen; pOff_ = 0; } void QGlobRegex::SetText(const char* text, std::size_t tlen) { text_ = text; tLen_ = tlen; tOff_ = 0; } bool QGlobRegex::TryMatch() { while (pOff_ < pLen_) { switch (pattern_[pOff_]) { case '*': return _ProcessStar(); case '?': if (!_ProcessQuestion()) return false; break; case '[': if (!_ProcessBracket()) return false; break; case '\\': if (pOff_ + 1 < pLen_ && pattern_[pOff_ + 1] == '[') ++ pOff_; // fall through; default: if (tOff_ >= tLen_) return false; if (pattern_[pOff_] != text_[tOff_]) return false; ++ pOff_; ++ tOff_; break; } } return _IsMatch(); } bool QGlobRegex::_ProcessStar() { assert(pattern_[pOff_] == '*'); do { ++ pOff_; } while (pOff_ < pLen_ && pattern_[pOff_] == '*'); if (pOff_ == pLen_) return true; while (tOff_ < tLen_) { std::size_t oldpoff = pOff_; std::size_t oldtoff = tOff_; if (TryMatch()) { return true; } pOff_ = oldpoff; tOff_ = oldtoff; ++ tOff_; } return false; } bool QGlobRegex::_ProcessQuestion() { assert(pattern_[pOff_] == '?'); while (pOff_ < pLen_) { if (pattern_[pOff_] != '?') break; if (tOff_ == tLen_) { return false; // str is too short } ++ pOff_; ++ tOff_; } return true; } bool QGlobRegex::_ProcessBracket() { assert(pattern_[pOff_] == '['); if (pOff_ + 1 >= pLen_) { //std::cerr << "expect ] at end\n"; return false; } ++ pOff_; bool include = true; if (pattern_[pOff_] == '^') { include = false; ++ pOff_; } std::set chars; if (pOff_ < pLen_ && pattern_[pOff_] == ']') { chars.insert(']'); // No allowed empty brackets. ++ pOff_; } std::set > spans; while (pOff_ < pLen_ && pattern_[pOff_] != ']') { if ((pOff_ + 3) < pLen_ && pattern_[pOff_ + 1] == '-') { int start = pattern_[pOff_]; int end = pattern_[pOff_ + 2]; if (start == end) { chars.insert(start); } else { if (start > end) std::swap(start, end); spans.insert(std::make_pair(start, end)); } pOff_ += 3; } else { chars.insert(pattern_[pOff_]); ++ pOff_; } } if (pOff_ == pLen_) { //std::cerr << "expect ]\n"; return false; } else { assert (pattern_[pOff_] == ']'); ++ pOff_; } if (chars.count(text_[tOff_]) > 0) { if (include) { ++ tOff_; return true; } else { return false; } } for (const auto& pair : spans) { if (text_[tOff_] >= pair.first && text_[tOff_] <= pair.second) { if (include) { ++ tOff_; return true; } else { return false; } } } if (include) { return false; } else { ++ tOff_; return true; } } bool QGlobRegex::_IsMatch() const { return pOff_ == pLen_ && tLen_ == tOff_; } } ================================================ FILE: QedisCore/QGlobRegex.h ================================================ #ifndef BERT_QGLOBREGEX_H #define BERT_QGLOBREGEX_H #include #include "QString.h" namespace qedis { bool NotGlobRegex(const char* pattern, std::size_t plen); class QGlobRegex { public: explicit QGlobRegex(const char* pattern = 0, std::size_t plen = 0, const char* text = 0, std::size_t tlen = 0); void SetPattern(const char* pattern, std::size_t plen); void SetText(const char* text, std::size_t tlen); bool TryMatch(); private: bool _ProcessStar(); bool _ProcessQuestion(); bool _ProcessBracket(); bool _IsMatch() const; const char* pattern_; std::size_t pLen_; std::size_t pOff_; const char* text_; std::size_t tLen_; std::size_t tOff_; }; inline bool glob_match(const char* pattern, std::size_t plen, const char* text, std::size_t tlen) { QGlobRegex rgx; rgx.SetPattern(pattern, plen); rgx.SetText(text, tlen); return rgx.TryMatch(); } inline bool glob_match(const char* pattern, const char* text) { return glob_match(pattern, strlen(pattern), text, strlen(text)); } inline bool glob_match(const QString& pattern, const QString& text) { return glob_match(pattern.c_str(), pattern.size(), text.c_str(), text.size()); } inline bool glob_search(const char* pattern, const char* str) { QString sPattern("*"); sPattern += pattern; sPattern += "*"; QGlobRegex rgx; rgx.SetPattern(sPattern.c_str(), sPattern.size()); rgx.SetText(str, strlen(str)); return rgx.TryMatch(); } } #endif ================================================ FILE: QedisCore/QHash.cc ================================================ #include "QHash.h" #include "QStore.h" #include namespace qedis { QObject QObject::CreateHash() { QObject obj(QType_hash); obj.Reset(new QHash); return obj; } #define GET_HASH(hashname) \ QObject* value; \ QError err = QSTORE.GetValueByType(hashname, value, QType_hash); \ if (err != QError_ok) { \ ReplyError(err, reply); \ return err; \ } #define GET_OR_SET_HASH(hashname) \ QObject* value; \ QError err = QSTORE.GetValueByType(hashname, value, QType_hash); \ if (err != QError_ok && err != QError_notExist) { \ ReplyError(err, reply); \ return err; \ } \ if (err == QError_notExist) { \ value = QSTORE.SetValue(hashname, QObject::CreateHash()); \ } QHash::iterator _set_hash_force(QHash& hash, const QString& key, const QString& val) { auto it(hash.find(key)); if (it != hash.end()) it->second = val; else it = hash.insert(QHash::value_type(key, val)).first; return it; } bool _set_hash_if_notexist(QHash& hash, const QString& key, const QString& val) { return hash.insert(QHash::value_type(key, val)).second; } QError hset(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_HASH(params[1]); auto hash= value->CastHash(); _set_hash_force(*hash, params[2], params[3]); FormatInt(1, reply); return QError_ok; } QError hmset(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 != 0) { ReplyError(QError_param, reply); return QError_param; } GET_OR_SET_HASH(params[1]); auto hash= value->CastHash(); for (size_t i = 2; i < params.size(); i += 2) _set_hash_force(*hash, params[i], params[i + 1]); FormatOK(reply); return QError_ok; } QError hget(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash= value->CastHash(); auto it = hash->find(params[2]); if (it != hash->end()) FormatBulk(it->second, reply); else FormatNull(reply); return QError_ok; } QError hmget(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); PreFormatMultiBulk(params.size() - 2, reply); auto hash= value->CastHash(); for (size_t i = 2; i < params.size(); ++ i) { auto it = hash->find(params[i]); if (it != hash->end()) FormatBulk(it->second, reply); else FormatNull(reply); } return QError_ok; } QError hgetall(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash = value->CastHash(); PreFormatMultiBulk(2 * hash->size(), reply); for (const auto& kv : *hash) { FormatBulk(kv.first, reply); FormatBulk(kv.second, reply); } return QError_ok; } QError hkeys(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash = value->CastHash(); PreFormatMultiBulk(hash->size(), reply); for (const auto& kv : *hash) { FormatBulk(kv.first, reply); } return QError_ok; } QError hvals(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash = value->CastHash(); PreFormatMultiBulk(hash->size(), reply); for (const auto& kv : *hash) { FormatBulk(kv.second, reply); } return QError_ok; } QError hdel(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_hash); if (err != QError_ok) { ReplyError(err, reply); return err; } int del = 0; auto hash = value->CastHash(); for (size_t i = 2; i < params.size(); ++ i) { auto it = hash->find(params[i]); if (it != hash->end()) { hash->erase(it); ++ del; } } FormatInt(del, reply); return QError_ok; } QError hexists(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash= value->CastHash(); auto it = hash->find(params[2]); if (it != hash->end()) FormatInt(1, reply); else FormatInt(0, reply); return QError_ok; } QError hlen(const std::vector& params, UnboundedBuffer* reply) { GET_HASH(params[1]); auto hash= value->CastHash(); FormatInt(hash->size(), reply); return QError_ok; } QError hincrby(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_HASH(params[1]); auto hash = value->CastHash(); long val = 0; QString* str = nullptr; auto it(hash->find(params[2])); if (it != hash->end()) { str = &it->second; if (Strtol(str->c_str(), static_cast(str->size()), &val)) { val += atoi(params[3].c_str()); } else { ReplyError(QError_nan, reply); return QError_nan; } } else { val = atoi(params[3].c_str()); auto it = _set_hash_force(*hash, params[2], ""); str = &it->second; } char tmp[32]; snprintf(tmp, sizeof tmp - 1, "%ld", val); *str = tmp; FormatInt(val, reply); return QError_ok; } QError hincrbyfloat(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_HASH(params[1]); auto hash = value->CastHash(); float val = 0; QString* str = 0; auto it(hash->find(params[2])); if (it != hash->end()) { str = &it->second; if (Strtof(str->c_str(), static_cast(str->size()), &val)) { val += atof(params[3].c_str()); } else { ReplyError(QError_param, reply); return QError_param; } } else { val = atof(params[3].c_str()); auto it = _set_hash_force(*hash, params[2], ""); str = &it->second; } char tmp[32]; snprintf(tmp, sizeof tmp - 1, "%f", val); *str = tmp; FormatBulk(*str, reply); return QError_ok; } QError hsetnx(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_HASH(params[1]); auto hash= value->CastHash(); if (_set_hash_if_notexist(*hash, params[2], params[3])) FormatInt(1, reply); else FormatInt(0, reply); return QError_ok; } QError hstrlen(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_hash); if (err != QError_ok) { Format0(reply); return err; } auto hash= value->CastHash(); auto it = hash->find(params[2]); if (it == hash->end()) Format0(reply); else FormatInt(static_cast(it->second.size()), reply); return QError_ok; } size_t HScanKey(const QHash& hash, size_t cursor, size_t count, std::vector& res) { if (hash.empty()) return 0; std::vector iters; size_t newCursor = ScanHashMember(hash, cursor, count, iters); res.reserve(iters.size()); for (auto it : iters) res.push_back(it->first), res.push_back(it->second); return newCursor; } } ================================================ FILE: QedisCore/QHash.h ================================================ #ifndef BERT_QHASH_H #define BERT_QHASH_H #include "QString.h" #include "QHelper.h" #include namespace qedis { using QHash = std::unordered_map >; size_t HScanKey(const QHash& hash, size_t cursor, size_t count, std::vector& res); } #endif ================================================ FILE: QedisCore/QHelper.cc ================================================ #include #include #include #include #include "QHelper.h" #if defined(__gnu_linux__) #include #elif defined(__APPLE__) #include #include #else #error "unknow platform" #endif namespace qedis { static unsigned dict_hash_function_seed = 5381; // hash func from redis unsigned int dictGenHashFunction(const void* key, int len) { /* 'm' and 'r' are mixing constants generated offline. They're not really 'magic', they just happen to work well. */ unsigned seed = dict_hash_function_seed; const unsigned m = 0x5bd1e995; const int r = 24; /* Initialize the hash to a 'random' value */ unsigned h = seed ^ len; /* Mix 4 bytes at a time into the hash */ const unsigned char *data = (const unsigned char *)key; while(len >= 4) { unsigned int k = *(unsigned int*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } /* Handle the last few bytes of the input array */ switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; /* Do a few final mixes of the hash to ensure the last few * bytes are well-incorporated. */ h ^= h >> 13; h *= m; h ^= h >> 15; return (unsigned int)h; } // hash function size_t my_hash::operator() (const QString& str) const { return dictGenHashFunction(str.data(), static_cast(str.size())); } static 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}; std::size_t BitCount(const uint8_t* buf, std::size_t len) { std::size_t cnt = 0; std::size_t loop = len / 4; std::size_t remain = len % 4; for (std::size_t i = 0; i < loop; ++ i) { cnt += bitsinbyte[buf[4 * i]]; cnt += bitsinbyte[buf[4 * i + 1]]; cnt += bitsinbyte[buf[4 * i + 2]]; cnt += bitsinbyte[buf[4 * i + 3]]; } for (std::size_t i = 0; i < remain; ++ i) { cnt += bitsinbyte[buf[4 * loop + i]]; } return cnt; } /* Copy from redis source. * Generate the Redis "Run ID", a SHA1-sized random number that identifies a * given execution of Redis, so that if you are talking with an instance * having run_id == A, and you reconnect and it has run_id == B, you can be * sure that it is either a different instance or it was restarted. */ void getRandomHexChars(char *p, unsigned int len) { FILE *fp = fopen("/dev/urandom","r"); const char *charset = "0123456789abcdef"; unsigned int j; if (fp == NULL || fread(p,len,1,fp) == 0) { /* If we can't read from /dev/urandom, do some reasonable effort * in order to create some entropy, since this function is used to * generate run_id and cluster instance IDs */ char *x = p; unsigned int l = len; struct timeval tv; pid_t pid = getpid(); /* Use time and PID to fill the initial array. */ gettimeofday(&tv,NULL); if (l >= sizeof(tv.tv_usec)) { memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec)); l -= sizeof(tv.tv_usec); x += sizeof(tv.tv_usec); } if (l >= sizeof(tv.tv_sec)) { memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec)); l -= sizeof(tv.tv_sec); x += sizeof(tv.tv_sec); } if (l >= sizeof(pid)) { memcpy(x,&pid,sizeof(pid)); l -= sizeof(pid); x += sizeof(pid); } /* Finally xor it with rand() output, that was already seeded with * time() at startup. */ for (j = 0; j < len; j++) p[j] ^= rand(); } /* Turn it into hex digits taking just 4 bits out of 8 for every byte. */ for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F]; fclose(fp); } #if defined(__gnu_linux__) /* /proc//status Field Description Option Explanation VmPeak peak virtual memory size VmSize This is the process's virtual set size, which is the amount of virtual memory that the application is using. It's the same as the vsz parameter provided by ps. VmLck This is the amount of memory that has been locked by this process. Locked memory cannot be swapped to disk. VmHWM : peak resident set size ("high water mark") VmRSS This 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. VmData This is the data size or the virtual size of the program's data usage. Unlike ps dsiz statistic, this does not include stack information. VmStk This is the size of the process's stack. VmExe This is the virtual size of the executable memory that the program has. It does not include libraries that the process is using. VmLib Size of shared library code. This is the size of the libraries that the process is using. VmSwap amount of swap used by anonymous private data(shmem swap usage is not included) */ std::vector getMemoryInfo() { /* VmPeak = 0, VmSize = 1, VmLck = 2, VmHWM = 3, VmRSS = 4, VmSwap = 5, */ std::vector res(VmMax); //int page = sysconf(_SC_PAGESIZE); char filename[64]; snprintf(filename, sizeof filename, "/proc/%d/status", getpid()); std::ifstream ifs(filename); std::string line; int count = 0; while (count < VmMax && std::getline(ifs, line)) { auto it(res.begin()); if (line.find("VmPeak") == 0) { ++ count; std::advance(it, VmPeak); } else if (line.find("VmSize") == 0) { ++ count; std::advance(it, VmSize); } else if (line.find("VmLck") == 0) { ++ count; std::advance(it, VmLck); } else if (line.find("VmHWM") == 0) { ++ count; std::advance(it, VmHWM); } else if (line.find("VmRSS") == 0) { ++ count; std::advance(it, VmRSS); } else if (line.find("VmSwap") == 0) { ++ count; std::advance(it, VmSwap); } else { continue; } // skip until number; std::size_t offset = 0; while (offset < line.size() && !isdigit(line[offset])) ++ offset; if (offset < line.size()) { char* end = nullptr; long val = strtol(&line[offset], &end, 0); val *= 1024; // since suffix is KB *it = static_cast(val); } } return res; } size_t getMemoryInfo(MemoryInfoType type) { char filename[64]; snprintf(filename, sizeof filename, "/proc/%d/status", getpid()); std::ifstream ifs(filename); std::string line; bool found = false; while (!found && std::getline(ifs, line)) { switch (type) { case VmPeak: if (line.find("VmPeak") == 0) found = true; break; case VmSize: if (line.find("VmSize") == 0) found = true; break; case VmLck: if (line.find("VmLck") == 0) found = true; break; case VmHWM: if (line.find("VmHWM") == 0) found = true; break; case VmRSS: if (line.find("VmRSS") == 0) found = true; break; case VmSwap: if (line.find("VmSwap") == 0) found = true; break; default: return 0; } } // skip until number; std::size_t offset = 0; while (offset < line.size() && !isdigit(line[offset])) ++ offset; size_t res = 0; if (offset < line.size()) { char* end = nullptr; long val = strtol(&line[offset], &end, 0); val *= 1024; // since suffix is KB res = static_cast(val); } return res; } #elif defined(__APPLE__) std::vector getMemoryInfo() { /* only support mach_vm_size_t virtual_size; virtual memory size (bytes) mach_vm_size_t resident_size; resident memory size (bytes) mach_vm_size_t resident_size_max; maximum resident memory size (bytes) */ std::vector res(VmMax); task_t task = MACH_PORT_NULL; struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) return res; task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); res[VmSize] = t_info.virtual_size; res[VmRSS] = t_info.resident_size; return res; } size_t getMemoryInfo(MemoryInfoType type) { if (type != VmSize && type != VmRSS) return 0; task_t task = MACH_PORT_NULL; struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) return 0; task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); if (type == VmSize) return t_info.virtual_size; else if (type == VmRSS) return t_info.resident_size; return 0; } #endif } ================================================ FILE: QedisCore/QHelper.h ================================================ #ifndef BERT_HELPER_H #define BERT_HELPER_H #include "QString.h" #include #include namespace qedis { // hash func from redis extern unsigned int dictGenHashFunction(const void* key, int len); // hash function struct my_hash { size_t operator() (const QString& str) const; }; std::size_t BitCount(const uint8_t* buf, std::size_t len); template inline typename HASH::const_local_iterator RandomHashMember(const HASH& container) { if (container.empty()) { return typename HASH::const_local_iterator(); } while (true) { size_t bucket = random() % container.bucket_count(); if (container.bucket_size(bucket) == 0) continue; long lucky = random() % container.bucket_size(bucket); typename HASH::const_local_iterator it = container.begin(bucket); while (lucky > 0) { ++ it; -- lucky; } return it; } return typename HASH::const_local_iterator(); } // scan template inline size_t ScanHashMember(const HASH& container, size_t cursor, size_t count, std::vector& res) { if (cursor >= container.size()) { return 0; } auto idx = cursor; for (decltype(container.bucket_count()) bucket = 0; bucket < container.bucket_count(); ++ bucket) { const auto bktSize = container.bucket_size(bucket); if (idx < bktSize) { // find the bucket; auto it = container.begin(bucket); while (idx > 0) { ++ it; -- idx; } size_t newCursor = cursor; auto end = container.end(bucket); while (res.size() < count && it != end) { ++ newCursor; res.push_back(it ++); if (it == end) { while (++ bucket < container.bucket_count()) { if (container.bucket_size(bucket) > 0) { it = container.begin(bucket); end = container.end(bucket); break; } } if (bucket == container.bucket_count()) return 0; } } return newCursor; } else { idx -= bktSize; } } return 0; // never here } extern void getRandomHexChars(char *p, unsigned int len); enum MemoryInfoType { VmPeak = 0, VmSize = 1, VmLck = 2, VmHWM = 3, VmRSS = 4, VmSwap = 5, VmMax = VmSwap + 1, }; extern std::vector getMemoryInfo(); extern size_t getMemoryInfo(MemoryInfoType type); } #endif ================================================ FILE: QedisCore/QKeyCommand.cc ================================================ #include "QStore.h" #include "Log/Logger.h" #include "QGlobRegex.h" #include namespace qedis { QError type(const std::vector& params, UnboundedBuffer* reply) { const char* info = 0; QType type = QSTORE.KeyType(params[1]); switch (type) { case QType_hash: info = "hash"; break; case QType_set: info = "set"; break; case QType_string: info = "string"; break; case QType_list: info = "list"; break; case QType_sortedSet: info = "sortedSet"; break; default: info = "none"; break; } FormatSingle(info, reply); return QError_ok; } QError exists(const std::vector& params, UnboundedBuffer* reply) { if (QSTORE.ExistsKey(params[1])) Format1(reply); else Format0(reply); return QError_ok; } QError del(const std::vector& params, UnboundedBuffer* reply) { int nDel = 0; for (size_t i = 1; i < params.size(); ++ i) { const QString& key = params[i]; if (QSTORE.DeleteKey(key)) { QSTORE.ClearExpire(key); ++ nDel; } } FormatInt(nDel, reply); return QError_ok; } static int _SetExpireByMs(const QString& key, uint64_t absTimeout) { INF << "try set expire, key " << key.c_str() << ", timeout is " << absTimeout; int ret = 0; if (QSTORE.ExistsKey(key)) { QSTORE.SetExpire(key, absTimeout); ret = 1; } return ret; } QError expire(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; const uint64_t timeout = atoi(params[2].c_str()); // by seconds; int ret = _SetExpireByMs(key, ::Now() + timeout * 1000); FormatInt(ret, reply); return QError_ok; } QError pexpire(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; const uint64_t timeout = atoi(params[2].c_str()); // by milliseconds; int ret = _SetExpireByMs(key, ::Now() + timeout); FormatInt(ret, reply); return QError_ok; } QError expireat(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; const uint64_t timeout = atoi(params[2].c_str()); // by seconds; int ret = _SetExpireByMs(key, timeout * 1000); FormatInt(ret, reply); return QError_ok; } QError pexpireat(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; const uint64_t timeout = atoi(params[2].c_str()); // by milliseconds; int ret = _SetExpireByMs(key, timeout); FormatInt(ret, reply); return QError_ok; } static int64_t _ttl(const QString& key) { int64_t ret = QStore::ExpireResult::notExist; if (QSTORE.ExistsKey(key)) { int64_t ttl = QSTORE.TTL(key, ::Now()); if (ttl < 0) ret = QStore::ExpireResult::persist; else ret = ttl; } else { ERR << "ttl not exist key:" << key.c_str(); } return ret; } QError ttl(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; int64_t ret = _ttl(key); if (ret > 0) ret /= 1000; // by seconds FormatInt(ret, reply); return QError_ok; } QError pttl(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; int64_t ret = _ttl(key); // by milliseconds FormatInt(ret, reply); return QError_ok; } QError persist(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; int ret = QSTORE.ClearExpire(key) ? 1 : 0; FormatInt(ret, reply); return QError_ok; } QError move(const std::vector& params, UnboundedBuffer* reply) { const QString& key = params[1]; int toDb = atoi(params[2].c_str()); int ret = 0; QObject* val; if (QSTORE.GetValue(key, val) == QError_ok) { int fromDb = QSTORE.SelectDB(toDb); if (fromDb >= 0 && fromDb != toDb && !QSTORE.ExistsKey(key)) { QSTORE.SelectDB(toDb); QSTORE.SetValue(key, std::move(*val)); // set to new db QSTORE.SelectDB(fromDb); QSTORE.ClearExpire(key); QSTORE.DeleteKey(key); // delete from old db ret = 1; INF << "move " << key << " to db " << toDb << ", from db " << fromDb; } else { ERR << "move " << key << " failed to db " << toDb << ", from db " << fromDb; } } else { ERR << "move " << key << " failed to db " << toDb; } FormatInt(ret, reply); return QError_ok; } QError keys(const std::vector& params, UnboundedBuffer* reply) { const QString& pattern = params[1]; std::vector results; for (const auto& kv : QSTORE) { if (glob_match(pattern, kv.first)) results.push_back(&kv.first); } PreFormatMultiBulk(results.size(), reply); for (auto e : results) { FormatBulk(*e, reply); } return QError_ok; } QError randomkey(const std::vector& params, UnboundedBuffer* reply) { const QString& res = QSTORE.RandomKey(); if (res.empty()) FormatNull(reply); else FormatBulk(res, reply); return QError_ok; } static QError RenameKey(const QString& oldKey, const QString& newKey, bool force) { QObject* val; QError err = QSTORE.GetValue(oldKey, val); if (err != QError_ok) return err; if (!force && QSTORE.ExistsKey(newKey)) return QError_exist; auto now = ::Now(); auto ttl = QSTORE.TTL(oldKey, now); if (ttl == QStore::expired) return QError_notExist; QSTORE.SetValue(newKey, std::move(*val)); if (ttl > 0) QSTORE.SetExpire(newKey, ttl + now); else if (ttl == QStore::persist) QSTORE.ClearExpire(newKey); QSTORE.ClearExpire(oldKey); QSTORE.DeleteKey(oldKey); return QError_ok; } QError rename(const std::vector& params, UnboundedBuffer* reply) { QError err = RenameKey(params[1], params[2], true); ReplyError(err, reply); return err; } QError renamenx(const std::vector& params, UnboundedBuffer* reply) { QError err = RenameKey(params[1], params[2], false); if (err == QError_ok) Format1(reply); else ReplyError(err, reply); return err; } // helper func scan static QError ParseScanOption(const std::vector& params, int start, long& count, const char*& pattern) { // scan cursor MATCH pattern COUNT 1 count = -1; pattern = nullptr; for (std::size_t i = start; i < params.size(); i += 2) { if (params[i].size() == 5) { if (strncasecmp(params[i].c_str(), "match", 5) == 0) { if (!pattern) { pattern = params[i + 1].c_str(); continue; } } else if (strncasecmp(params[i].c_str(), "count", 5) == 0) { if (count == -1) { if (Strtol(params[i+1].c_str(), params[i+1].size(), &count)) continue; } } } return QError_param; } return QError_ok; } QError scan(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 != 0) { ReplyError(QError_param, reply); return QError_param; } long cursor = 0; if (!Strtol(params[1].c_str(), params[1].size(), &cursor)) { ReplyError(QError_param, reply); return QError_param; } // scan cursor MATCH pattern COUNT 1 long count = -1; const char* pattern = nullptr; QError err = ParseScanOption(params, 2, count, pattern); if (err != QError_ok) { ReplyError(err, reply); return err; } if (count < 0) count = 5; std::vector res; auto newCursor = QSTORE.ScanKey(cursor, count, res); // filter by pattern if (pattern) { for (auto it = res.begin(); it != res.end(); ) { if (!glob_match(pattern, (*it).c_str())) it = res.erase(it); else ++ it; } } // reply PreFormatMultiBulk(2, reply); char buf[32]; auto len = snprintf(buf, sizeof buf -1, "%lu", newCursor); FormatBulk(buf, len, reply); PreFormatMultiBulk(res.size(), reply); for (const auto& s : res) { FormatBulk(s, reply); } return QError_ok; } QError hscan(const std::vector& params, UnboundedBuffer* reply) { // hscan key cursor COUNT 0 MATCH 0 if (params.size() % 2 == 0) { ReplyError(QError_param, reply); return QError_param; } long cursor = 0; if (!Strtol(params[2].c_str(), params[2].size(), &cursor)) { ReplyError(QError_param, reply); return QError_param; } // find hash QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_hash); if (err != QError_ok) { ReplyError(err, reply); return err; } // parse option long count = -1; const char* pattern = nullptr; err = ParseScanOption(params, 3, count, pattern); if (err != QError_ok) { ReplyError(err, reply); return err; } if (count < 0) count = 5; // scan std::vector res; auto newCursor = HScanKey(*value->CastHash(), cursor, count, res); // filter by pattern if (pattern) { for (auto it = res.begin(); it != res.end(); ) { if (!glob_match(pattern, (*it).c_str())) { it = res.erase(it); // erase key it = res.erase(it); // erase value } else { ++ it, ++ it; } } } // reply PreFormatMultiBulk(2, reply); char buf[32]; auto len = snprintf(buf, sizeof buf -1, "%lu", newCursor); FormatBulk(buf, len, reply); PreFormatMultiBulk(res.size(), reply); for (const auto& s : res) { FormatBulk(s, reply); } return QError_ok; } QError sscan(const std::vector& params, UnboundedBuffer* reply) { // sscan key cursor COUNT 0 MATCH 0 if (params.size() % 2 == 0) { ReplyError(QError_param, reply); return QError_param; } long cursor = 0; if (!Strtol(params[2].c_str(), params[2].size(), &cursor)) { ReplyError(QError_param, reply); return QError_param; } // find set QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_set); if (err != QError_ok) { ReplyError(err, reply); return err; } // parse option long count = -1; const char* pattern = nullptr; err = ParseScanOption(params, 3, count, pattern); if (err != QError_ok) { ReplyError(err, reply); return err; } if (count < 0) count = 5; // scan std::vector res; auto newCursor = SScanKey(*value->CastSet(), cursor, count, res); // filter by pattern if (pattern) { for (auto it = res.begin(); it != res.end(); ) { if (!glob_match(pattern, (*it).c_str())) { it = res.erase(it); } else { ++ it; } } } // reply PreFormatMultiBulk(2, reply); char buf[32]; auto len = snprintf(buf, sizeof buf -1, "%lu", newCursor); FormatBulk(buf, len, reply); PreFormatMultiBulk(res.size(), reply); for (const auto& s : res) { FormatBulk(s, reply); } return QError_ok; } QError sort(const std::vector& params, UnboundedBuffer* reply) { // sort key desc/asc alpha QObject* value; QError err = QSTORE.GetValue(params[1], value); if (err != QError_ok) { ReplyError(err, reply); return err; } if (value->type != QType_list && value->type != QType_set && value->type != QType_sortedSet) { ReplyError(QError_type, reply); return QError_type; } bool asc = true; bool alpha = false; for (const auto& arg : params) { if (strncasecmp(arg.data(), "desc", 4) == 0) asc = false; else if (strncasecmp(arg.data(), "alpha", 5) == 0) alpha = true; } std::vector values; switch (value->type) { case QType_list: { PLIST l = value->CastList(); std::for_each(l->begin(), l->end(), [&](const QString& v) { values.push_back(&v); }); } break; case QType_set: { PSET s = value->CastSet(); std::for_each(s->begin(), s->end(), [&](const QString& v) { values.push_back(&v); }); } break; case QType_sortedSet: { // TODO FormatOK(reply); return QError_ok; } break; default: break; } std::sort(values.begin(), values.end(), [=](const QString* a, const QString* b)->bool { if (!alpha) { long avalue = 0, bvalue = 0; TryStr2Long(a->data(), a->size(), avalue); TryStr2Long(b->data(), b->size(), bvalue); if (asc) return avalue < bvalue; else return bvalue < avalue; } else { if (asc) return std::lexicographical_compare(a->begin(), a->end(), b->begin(), b->end()); else return std::lexicographical_compare(b->begin(), b->end(), a->begin(), a->end()); } }); PreFormatMultiBulk(values.size(), reply); for (const auto v : values) { FormatBulk(*v, reply); } return QError_ok; } } ================================================ FILE: QedisCore/QLeveldb.cc ================================================ #include "QLeveldb.h" #include "leveldb/db.h" #include "Log/Logger.h" #include "UnboundedBuffer.h" namespace qedis { QLeveldb::QLeveldb() : db_(nullptr) { } QLeveldb::~QLeveldb() { delete db_; } bool QLeveldb::IsOpen() const { return db_ != nullptr; } bool QLeveldb::Open(const char* path) { leveldb::Options options; options.create_if_missing = true; // options.error_if_exists = true; auto s = leveldb::DB::Open(options, path, &db_); if (!s.ok()) { ERR << "Open db_ failed:" << s.ToString(); } return s.ok(); } QObject QLeveldb::Get(const QString& key) { std::string value; auto status = db_->Get(leveldb::ReadOptions(), leveldb::Slice(key.data(), key.size()), &value); if (!status.ok()) return QObject(QType_invalid); int64_t remainTtlSeconds = 0; QObject obj = _DecodeObject(value.data(), value.size(), remainTtlSeconds); // trick: use obj.lru to store the remain seconds to be expired. if (remainTtlSeconds > 0) obj.lru = static_cast(remainTtlSeconds); else obj.lru = 0; return obj; } bool QLeveldb::Put(const QString& key) { QObject* obj; QError ok = QSTORE.GetValue(key, obj, false); if (ok != QError_ok) return false; uint64_t now = ::Now(); int64_t ttl = QSTORE.TTL(key, now); if (ttl > 0) ttl += now; else if (ttl == QStore::ExpireResult::expired) return false; return Put(key, *obj, ttl); } bool QLeveldb::Put(const QString& key, const QObject& obj, int64_t absttl) { UnboundedBuffer v; _EncodeObject(obj, absttl, v); leveldb::Slice lkey(key.data(), key.size()); leveldb::Slice lval(v.ReadAddr(), v.ReadableSize()); auto s = db_->Put(leveldb::WriteOptions(), lkey, lval); return s.ok(); } bool QLeveldb::Delete(const QString& key) { leveldb::Slice lkey(key.data(), key.size()); auto s = db_->Delete(leveldb::WriteOptions(), lkey); return s.ok(); } void QLeveldb::_EncodeObject(const QObject& obj, int64_t absttl, UnboundedBuffer& v) { // value format: | ttl flag 1byte| ttl 8bytes if has|type 1byte| object contents // write ttl, if has int8_t ttlflag = (absttl > 0 ? 1 : 0); v.Write(&ttlflag, sizeof ttlflag); if (ttlflag) v.Write(&absttl, sizeof absttl); // write type int8_t type = obj.type; v.Write(&type, sizeof type); switch (obj.encoding) { case QEncode_raw: case QEncode_int: { auto str = GetDecodedString(&obj); _EncodeString(*str, v); } break; case QEncode_list: _EncodeList(obj.CastList(), v); break; case QEncode_set: _EncodeSet(obj.CastSet(), v); break; case QEncode_hash: _EncodeHash(obj.CastHash(), v); break; case QEncode_sset: _EncodeSSet(obj.CastSortedSet(), v); break; default: break; } } void QLeveldb::_EncodeString(const QString& str, UnboundedBuffer& v) { // write size auto len = static_cast(str.size()); v.Write(&len, 4); // write content v.Write(str.data(), len); } void QLeveldb::_EncodeHash(const PHASH& h, UnboundedBuffer& v) { // write size auto len = static_cast(h->size()); v.Write(&len, 4); for (const auto& e : *h) { _EncodeString(e.first, v); _EncodeString(e.second, v); } } void QLeveldb::_EncodeList(const PLIST& l, UnboundedBuffer& v) { // write size auto len = static_cast(l->size()); v.Write(&len, 4); for (const auto& e : *l) { _EncodeString(e, v); } } void QLeveldb::_EncodeSet(const PSET& s, UnboundedBuffer& v) { auto len = static_cast(s->size()); v.Write(&len, 4); for (const auto& e : *s) { _EncodeString(e, v); } } void QLeveldb::_EncodeSSet(const PSSET& ss, UnboundedBuffer& v) { auto len = static_cast(ss->Size()); v.Write(&len, 4); for (const auto& e : *ss) { _EncodeString(e.first, v); auto s(std::to_string(e.second)); _EncodeString(s, v); } } QObject QLeveldb::_DecodeObject(const char* data, size_t len, int64_t& remainTtl) { // | type 1byte | ttl flag 1byte| ttl 8bytes, if has| remainTtl = 0; size_t offset = 0; int8_t hasttl = *(int8_t*)(data + offset); offset += sizeof hasttl; int64_t absttl = 0; if (hasttl) { absttl = *(int64_t*)(data + offset); offset += sizeof absttl; } if (absttl != 0) { int64_t now = static_cast(::Now()); if (absttl <= now) { DBG << "Load from leveldb is timeout " << absttl; return QObject(QType_invalid); } else { // Only support seconds, because lru is 24bits, too short. remainTtl = (absttl - now) / 1000; INF << "Load from leveldb remainTtlSeconds: " << remainTtl; } } int8_t type = *(int8_t*)(data + offset); offset += sizeof type; switch (type) { case QType_string: { return QObject::CreateString(_DecodeString(data + offset, len - offset)); } case QType_list: { return _DecodeList(data + offset, len - offset); } case QType_set: { return _DecodeSet(data + offset, len - offset); } case QType_sortedSet: { return _DecodeSSet(data + offset, len - offset); } case QType_hash: { return _DecodeHash(data + offset, len - offset); } default: break; } assert(false); return QObject(QType_invalid); } QString QLeveldb::_DecodeString(const char* data, size_t len) { assert(len > 4); // read length uint32_t slen = *(uint32_t*)(data); // read content const char* sdata = data + 4; return QString(sdata, slen); } QObject QLeveldb::_DecodeHash(const char* data, size_t len) { assert(len >= 4); uint32_t hlen = *(uint32_t*)(data); QObject obj(QObject::CreateHash()); PHASH hash(obj.CastHash()); size_t offset = 4; for (uint32_t i = 0; i < hlen; ++ i) { auto key = _DecodeString(data + offset, len - offset); offset += key.size() + 4; auto value = _DecodeString(data + offset, len - offset); offset += value.size() + 4; hash->insert(QHash::value_type(key, value)); DBG << "Load from leveldb: hash key : " << key << " val : " << value; } return obj; } QObject QLeveldb::_DecodeList(const char* data, size_t len) { assert(len >= 4); uint32_t llen = *(uint32_t*)(data); QObject obj(QObject::CreateList()); PLIST list(obj.CastList()); size_t offset = 4; for (uint32_t i = 0; i < llen; ++ i) { auto elem = _DecodeString(data + offset, len - offset); offset += elem.size() + 4; list->push_back(elem); DBG << "Load list elem from leveldb: " << elem; } return obj; } QObject QLeveldb::_DecodeSet(const char* data, size_t len) { assert(len >= 4); uint32_t slen = *(uint32_t*)(data); QObject obj(QObject::CreateSet()); PSET set(obj.CastSet()); size_t offset = 4; for (uint32_t i = 0; i < slen; ++ i) { auto elem = _DecodeString(data + offset, len - offset); offset += elem.size() + 4; set->insert(elem); DBG << "Load set elem from leveldb: " << elem; } return obj; } QObject QLeveldb::_DecodeSSet(const char* data, size_t len) { assert(len >= 4); uint32_t sslen = *(uint32_t*)(data); QObject obj(QObject::CreateSSet()); PSSET sset(obj.CastSortedSet()); size_t offset = 4; for (uint32_t i = 0; i < sslen; ++ i) { auto member = _DecodeString(data + offset, len - offset); offset += member.size() + 4; auto scoreStr = _DecodeString(data + offset, len - offset); offset += scoreStr.size() + 4; double score = std::stod(scoreStr); sset->AddMember(member, static_cast(score)); DBG << "Load leveldb sset member : " << member << " score : " << score; } return obj; } } ================================================ FILE: QedisCore/QLeveldb.h ================================================ #ifndef BERT_QLEVELDB_H #define BERT_QLEVELDB_H #include "QDumpInterface.h" #include "QStore.h" namespace leveldb { class DB; } namespace qedis { class UnboundedBuffer; class QLeveldb : public QDumpInterface { public: QLeveldb(); ~QLeveldb(); bool Open(const char* path); bool IsOpen() const ; QObject Get(const QString& key) override; bool Put(const QString& key) override; bool Put(const QString& key, const QObject& obj, int64_t ttl = 0) override; bool Delete(const QString& key) override; private: leveldb::DB* db_; // encoding stuff // value format: type + ttl(if has) + qobject void _EncodeObject(const QObject& obj, int64_t absttl, UnboundedBuffer& v); void _EncodeString(const QString& str, UnboundedBuffer& v); void _EncodeHash(const PHASH& , UnboundedBuffer& v); void _EncodeList(const PLIST& , UnboundedBuffer& v); void _EncodeSet(const PSET& , UnboundedBuffer& v); void _EncodeSSet(const PSSET& , UnboundedBuffer& v); // decoding stuff QObject _DecodeObject(const char* data, size_t len, int64_t& remainTtlSeconds); QString _DecodeString(const char* data, size_t len); QObject _DecodeHash(const char* data, size_t len); QObject _DecodeList(const char* data, size_t len); QObject _DecodeSet(const char* data, size_t len); QObject _DecodeSSet(const char* data, size_t len); }; } #endif ================================================ FILE: QedisCore/QList.cc ================================================ #include "QList.h" #include "QStore.h" #include "QClient.h" #include "Log/Logger.h" #include #include using std::vector; namespace qedis { QObject QObject::CreateList() { QObject list(QType_list); list.Reset(new QList); return list; } static QError push(const vector& params, UnboundedBuffer* reply, ListPosition pos, bool createIfNotExist = true) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { if (err != QError_notExist) { ReplyError(err, reply); return err; } else if (createIfNotExist) { value = QSTORE.SetValue(params[1], QObject::CreateList()); } else { ReplyError(err, reply); return err; } } auto list = value->CastList(); bool mayReady = list->empty(); for (size_t i = 2; i < params.size(); ++ i) { if (pos == ListPosition::head) list->push_front(params[i]); else list->push_back(params[i]); } FormatInt(static_cast(list->size()), reply); if (mayReady && !list->empty()) { if (reply) // Do not propogate if aof reload... { // push must before pop(serve)... Propogate(params); // the push QSTORE.ServeClient(params[1], list); // the pop } return QError_nop; } else { return QError_ok; } } static QError GenericPop(const QString& key, ListPosition pos, QString& result) { QObject* value; QError err = QSTORE.GetValueByType(key, value, QType_list); if (err != QError_ok) { return err; } auto list = value->CastList(); assert (!list->empty()); if (pos == ListPosition::head) { result = std::move(list->front()); list->pop_front(); } else { result = std::move(list->back()); list->pop_back(); } if (list->empty()) { QSTORE.DeleteKey(key); } return QError_ok; } QError lpush(const vector& params, UnboundedBuffer* reply) { return push(params, reply, ListPosition::head); } QError rpush(const vector& params, UnboundedBuffer* reply) { return push(params, reply, ListPosition::tail); } QError lpushx(const vector& params, UnboundedBuffer* reply) { return push(params, reply, ListPosition::head, false); } QError rpushx(const vector& params, UnboundedBuffer* reply) { return push(params, reply, ListPosition::tail, false); } QError lpop(const vector& params, UnboundedBuffer* reply) { QString result; QError err = GenericPop(params[1], ListPosition::head, result); switch (err) { case QError_ok: FormatBulk(result, reply); break; default: ReplyError(err, reply); break; } return err; } QError rpop(const vector& params, UnboundedBuffer* reply) { QString result; QError err = GenericPop(params[1], ListPosition::tail, result); switch (err) { case QError_ok: FormatBulk(result, reply); break; default: ReplyError(err, reply); break; } return err; } static bool _BlockClient( QClient* client, const QString& key, uint64_t timeout, ListPosition pos, const QString* dstList = 0) { auto now = ::Now(); if (timeout > 0) timeout += now; else timeout = std::numeric_limits::max(); return QSTORE.BlockClient(key, client, timeout, pos, dstList); } static QError _GenericBlockedPop(vector::const_iterator keyBegin, vector::const_iterator keyEnd, UnboundedBuffer* reply, ListPosition pos, long timeout, const QString* target = nullptr, bool withKey = true) { for (auto it(keyBegin); it != keyEnd; ++ it) { QString result; QError err = GenericPop(*it, pos, result); switch (err) { case QError_ok: if (withKey) { PreFormatMultiBulk(2, reply); FormatBulk(*it, reply); } FormatBulk(result, reply); if (target) { // the target process } { std::vector params; params.push_back(pos == ListPosition::head ? "lpop" : "rpop"); params.push_back(*it); QClient::Current()->RewriteCmd(params); } return err; case QError_type: ReplyError(err, reply); return err; case QError_notExist: break; default: assert(!!!"Unknow error"); } } // Do NOT block if in transaction if (QClient::Current() && QClient::Current()->IsFlagOn(ClientFlag_multi)) { FormatNull(reply); return QError_nop; } // Put client to the wait-list for (auto it(keyBegin); it != keyEnd; ++ it) { _BlockClient(QClient::Current(), *it, timeout, pos, target); } return QError_nop; } QError blpop(const vector& params, UnboundedBuffer* reply) { long timeout; if (!TryStr2Long(params.back().c_str(), params.back().size(), timeout)) { ReplyError(QError_nan, reply); return QError_nan; } timeout *= 1000; return _GenericBlockedPop(++ params.begin(), -- params.end(), reply, ListPosition::head, timeout); } QError brpop(const vector& params, UnboundedBuffer* reply) { long timeout; if (!TryStr2Long(params.back().c_str(), params.back().size(), timeout)) { ReplyError(QError_nan, reply); return QError_nan; } timeout *= 1000; return _GenericBlockedPop(++ params.begin(), -- params.end(), reply, ListPosition::tail, timeout); } QError lindex(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { FormatNull(reply); return err; } long idx; if (!TryStr2Long(params[2].c_str(), params[2].size(), idx)) { ReplyError(QError_nan, reply); return QError_nan; } auto list = value->CastList(); const int size = static_cast(list->size()); if (idx < 0) idx += size; if (idx < 0 || idx >= size) { FormatNull(reply); return QError_ok; } const QString* result = nullptr; if (2 * idx < size) { auto it = list->begin(); std::advance(it, idx); result = &*it; } else { auto it = list->rbegin(); idx = size - 1 - idx; std::advance(it, idx); result = &*it; } FormatBulk(*result, reply); return QError_ok; } QError lset(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { ReplyError(QError_notExist, reply); return err; } auto list = value->CastList(); long idx; if (!TryStr2Long(params[2].c_str(), params[2].size(), idx)) { ReplyError(QError_param, reply); return QError_notExist; } const int size = static_cast(list->size()); if (idx < 0) idx += size; if (idx < 0 || idx >= size) { FormatNull(reply); return QError_ok; } QString* result = nullptr; if (2 * idx < size) { auto it = list->begin(); std::advance(it, idx); result = &*it; } else { auto it = list->rbegin(); idx = size - 1 - idx; std::advance(it, idx); result = &*it; } *result = params[3]; FormatOK(reply); return QError_ok; } QError llen(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { if (err == QError_type) ReplyError(err, reply); else Format0(reply); return err; } auto list = value->CastList(); FormatInt(static_cast(list->size()), reply); return QError_ok; } static void Index2Iterator(long start, long end, QList& list, QList::iterator* beginIt, QList::iterator* endIt) { assert (start >= 0 && end >= 0 && start <= end); assert (end < static_cast(list.size())); long size = static_cast(list.size()); if (beginIt) { if (start * 2 < size) { *beginIt = list.begin(); while (start -- > 0) ++ *beginIt; } else { *beginIt = list.end(); while (start ++ < size) -- *beginIt; } } if (endIt) { if (end * 2 < size) { *endIt = list.begin(); while (end -- > 0) ++ *endIt; } else { *endIt = list.end(); while (end ++ < size) -- *endIt; } } } static size_t GetRange(long start, long end, QList& list, QList::iterator* beginIt = nullptr, QList::iterator* endIt = nullptr) { size_t rangeLen = 0; if (start > end) // empty { if (beginIt) *beginIt = list.end(); if (endIt) *endIt = list.end(); } else if (start != 0 || end + 1 != static_cast(list.size())) { rangeLen = end - start + 1; Index2Iterator(start, end, list, beginIt, endIt); } else { rangeLen = list.size(); if (beginIt) *beginIt = list.begin(); if (endIt) *endIt = -- list.end(); // entire list } return rangeLen; } QError ltrim(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { ReplyError(err, reply); return err; } long start, end; if (!Strtol(params[2].c_str(), params[2].size(), &start) || !Strtol(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_param, reply); return err; } auto list = value->CastList(); AdjustIndex(start, end, list->size()); QList::iterator beginIt, endIt; GetRange(start, end, *list, &beginIt, &endIt); if (beginIt != list->end()) { assert (endIt != list->end()); list->erase(list->begin(), beginIt); list->erase(++ endIt, list->end()); } FormatOK(reply); return QError_ok; } QError lrange(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { ReplyError(err, reply); return err; } long start, end; if (!Strtol(params[2].c_str(), params[2].size(), &start) || !Strtol(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_param, reply); return err; } auto list = value->CastList(); AdjustIndex(start, end, list->size()); QList::iterator beginIt; size_t rangeLen = GetRange(start, end, *list, &beginIt); PreFormatMultiBulk(rangeLen, reply); if (beginIt != list->end()) { while (rangeLen != 0) { FormatBulk(beginIt->c_str(), beginIt->size(), reply); ++ beginIt; -- rangeLen; } } return QError_ok; } QError linsert(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { Format0(reply); return err; } bool before = false; if (params[2] == "before") before = true; else if (params[2] == "after") before = false; else { ReplyError(QError_param, reply); return QError_param; } auto list = value->CastList(); QList::iterator it = std::find(list->begin(), list->end(), params[3]); if (it == list->end()) { FormatInt(-1, reply); return QError_notExist; } if (before) list->insert(it, params[4]); else list->insert(++ it, params[4]); FormatInt(static_cast(list->size()), reply); return QError_ok; } QError lrem(const vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_list); if (err != QError_ok) { Format0(reply); return err; } long count; if (!Strtol(params[2].c_str(), params[2].size(), &count)) { ReplyError(QError_param, reply); return err; } auto list = value->CastList(); ListPosition start = ListPosition::head; if (count < 0) { count = -count; start = ListPosition::tail; } else if (count == 0) { count = list->size(); // remove all elements equal to param[3] } long resultCount = 0; if (start == ListPosition::head) { auto it = list->begin(); while (it != list->end() && resultCount < count) { if (*it == params[3]) { list->erase(it ++); ++ resultCount; } else { ++ it; } } } else { auto it = list->rbegin(); while (it != list->rend() && resultCount < count) { if (*it == params[3]) { list->erase((++it).base()); // Effective STL, item 28 ++ resultCount; } else { ++ it; } } } FormatInt(resultCount, reply); return QError_ok; } QError rpoplpush(const vector& params, UnboundedBuffer* reply) { QObject* src; QError err = QSTORE.GetValueByType(params[1], src, QType_list); if (err != QError_ok) { FormatNull(reply); return err; } auto srclist = src->CastList(); assert (!srclist->empty()); QObject* dst; err = QSTORE.GetValueByType(params[2], dst, QType_list); if (err != QError_ok) { if (err != QError_notExist) { ReplyError(err, reply); return err; } dst = QSTORE.SetValue(params[2], QObject::CreateList()); } auto dstlist = dst->CastList(); dstlist->splice(dstlist->begin(), *srclist, (++ srclist->rbegin()).base()); FormatBulk(*(dstlist->begin()), reply); return QError_ok; } QError brpoplpush(const vector& params, UnboundedBuffer* reply) { // check timeout format long timeout; if (!TryStr2Long(params.back().c_str(), params.back().size(), timeout)) { ReplyError(QError_nan, reply); return QError_nan; } timeout *= 1000; // check target list QObject* dst; QError err = QSTORE.GetValueByType(params[2], dst, QType_list); if (err != QError_ok) { if (err != QError_notExist) { ReplyError(err, reply); return err; } } auto dstKeyIter = -- (-- params.end()); return _GenericBlockedPop(++ params.begin(), dstKeyIter, reply, ListPosition::tail, timeout, &*dstKeyIter, false); } } ================================================ FILE: QedisCore/QList.h ================================================ #ifndef BERT_QLIST_H #define BERT_QLIST_H #include "QString.h" #include namespace qedis { enum class ListPosition { head, tail, }; using QList = std::list; } #endif ================================================ FILE: QedisCore/QMigration.cc ================================================ #include "Server.h" #include "QMigration.h" #include "Timer.h" #include "QDB.h" namespace qedis { static void ProcessItem(MigrationItem& item, QMigrateClient* conn) { UnboundedBuffer request; for (auto it = item.keys.begin(); it != item.keys.end(); ) { const auto& key = *it; if (key.empty()) { it = item.keys.erase(it); continue; } const QObject* obj = QSTORE.GetObject(key); if (!obj) { it = item.keys.erase(it); continue; } std::string contents = DumpObject(*obj); if (item.replace) { PreFormatMultiBulk(5, &request); FormatMultiBulk({"restore", key, "0", contents, "replace"}, &request); } else { PreFormatMultiBulk(4, &request); FormatMultiBulk({"restore", key, "0", contents}, &request); } ++ it; } if (!request.IsEmpty()) { UnboundedBuffer select; PreFormatMultiBulk(2, &select); FormatMultiBulk({"select", std::to_string(item.dstDb)}, &select); conn->SendPacket(select.ReadAddr(), select.ReadableSize()); conn->SendPacket(request.ReadAddr(), request.ReadableSize()); item.state = MigrateState::Processing; } else { item.state = MigrateState::Done; auto c = item.client.lock(); if (c) c->SendPacket("+NOKEY\r\n", 8); } } QMigrationManager& QMigrationManager::Instance() { static QMigrationManager mgr; return mgr; } void QMigrationManager::Add(const MigrationItem& item) { items_[item.dst].push_back(item); } void QMigrationManager::Add(MigrationItem&& item) { items_[item.dst].push_back(std::move(item)); } void QMigrationManager::InitMigrationTimer() { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(20); timer->SetCallback([]() { QMigrationManager::Instance().LoopCheck(); }); TimerManager::Instance().AddTimer(timer); } void QMigrationManager::LoopCheck() { auto now = ::time(nullptr); for (auto iter(items_.begin()); iter != items_.end(); ) { auto& items = iter->second; for (auto it = items.begin(); it != items.end(); ) { auto& item = *it; if (item.state != MigrateState::Done && now > item.timeout) item.state = MigrateState::Timeout; bool erased = false; switch (item.state) { case MigrateState::None: { auto c = GetConnection(item.dst); if (c) ProcessItem(item, c.get()); } break; case MigrateState::Processing: //waiting.... break; case MigrateState::Timeout: { auto c = item.client.lock(); if (c) { const char err[] = "-IOERR error or timeout for target instance\r\n"; c->SendPacket(err, sizeof err - 1); item.state = MigrateState::Done; } } case MigrateState::Done: it = items.erase(it); erased = true; break; default: assert(false); break; } if (!erased) ++ it; } if (items.empty()) iter = items_.erase(iter); else ++ iter; } } std::shared_ptr QMigrationManager::GetConnection(const SocketAddr& dst) { auto it = conns_.find(dst); if (it != conns_.end()) { auto c = it->second.lock(); if (c) return c; else conns_.erase(it); } if (pendingConnect_.count(dst)) return nullptr; // already connecting pendingConnect_.insert(dst); Server::Instance()->TCPConnect(dst, std::bind(&QMigrationManager::OnConnectMigrateFail, this, dst), ConnectionTag::kMigrateClient); return nullptr; } void QMigrationManager::OnConnectMigrateFail(const SocketAddr& dst) { pendingConnect_.erase(dst); auto it = items_.find(dst); if (it == items_.end()) return; for (auto& item : it->second) { if (item.state == MigrateState::Done) continue; // can not ensure success, so retry if (item.state == MigrateState::Processing) item.state = MigrateState::None; } } void QMigrationManager::OnConnect(QMigrateClient* client) { const auto& dst = client->GetPeerAddr(); std::weak_ptr wc = std::static_pointer_cast(client->shared_from_this()); bool succ = conns_.insert({dst, wc}).second; assert (succ); size_t tmp = pendingConnect_.erase(dst); assert (tmp == 1); auto it = items_.find(dst); if (it == items_.end()) client->OnError(); } void QMigrateClient::OnConnect() { auto& mgr = QMigrationManager::Instance(); mgr.OnConnect(this); auto it = mgr.items_.find(peerAddr_); assert (it != mgr.items_.end()); for (auto& item : it->second) { auto c = item.client.lock(); if (!c) { item.state = MigrateState::Done; continue; } if (item.state == MigrateState::None) { ProcessItem(item, this); } } } PacketLength QMigrateClient::_HandlePacket(const char* msg, std::size_t len) { if (len < 5) return 0; const char* crlf = SearchCRLF(msg, len); if (!crlf) return 0; ++ readyRsp_; auto it = QMigrationManager::Instance().items_.find(peerAddr_); auto& item = it->second.front(); if (item.keys.size() + 1 == readyRsp_) // plus select db { readyRsp_ = 0; auto c = item.client.lock(); if (c) c->SendPacket("+OK\r\n", 5); if (!item.copy) { std::vector params {"del"}; params.insert(params.end(), item.keys.begin(), item.keys.end()); extern QError del(const std::vector& , UnboundedBuffer* ); del(params, nullptr); Propogate(params); } item.state = MigrateState::Done; } return static_cast(crlf + 2 - msg); } // migrate host port key dst-db timeout COPY REPLACE KEYS key1 key2 QError migrate(const std::vector& params, UnboundedBuffer* reply) { try { struct MigrationItem item; item.dst.Init(params[1].data(), std::stoi(params[2])); item.dstDb = std::stoi(params[4]); item.timeout = ::time(nullptr) + (std::stoi(params[5]) + 999) / 1000; const int kUnused = -1; int copy = kUnused; int replace = kUnused; bool withKeys = false; for (size_t i = 6; i < params.size(); ++ i) { if (withKeys) { item.keys.push_back(params[i]); } else { // must be COPY REPLACE or KEYS if (strncasecmp(params[i].c_str(), "copy", 4)) { if (copy == kUnused) copy = 1; else throw std::runtime_error("wrong syntax for migrate"); } else if (strncasecmp(params[i].c_str(), "replace", 7)) { if (replace == kUnused) replace = 1; else throw std::runtime_error("wrong syntax for migrate"); } else if (strncasecmp(params[i].c_str(), "keys", 4)) { withKeys = true; } else { throw std::runtime_error("wrong syntax for migrate"); } } } if (!withKeys && !params[3].empty()) item.keys.push_back(params[3]); if (copy == 1) item.copy = true; if (replace == 1) item.replace = true; item.client = std::static_pointer_cast(QClient::Current()->shared_from_this()); QMigrationManager::Instance().Add(std::move(item)); return QError_ok; } catch (...) { ReplyError(QError_syntax, reply); return QError_syntax; } return QError_ok; } } // end namespace qedis ================================================ FILE: QedisCore/QMigration.h ================================================ #ifndef BERT_QMIGRATION_H #define BERT_QMIGRATION_H #include #include "QClient.h" namespace ConnectionTag { const int kMigrateClient = 4; } namespace qedis { enum class MigrateState { None, Processing, Timeout, Done, }; struct MigrationItem { SocketAddr dst; int dstDb = 0; time_t timeout = 0; bool copy = false; bool replace = false; std::vector keys; std::weak_ptr client; MigrateState state = MigrateState::None; }; class QMigrateClient; class QMigrationManager { friend class QMigrateClient; public: static QMigrationManager& Instance(); void Add(const MigrationItem& item); void Add(MigrationItem&& item); void InitMigrationTimer(); void LoopCheck(); std::shared_ptr GetConnection(const SocketAddr& dst); void OnConnectMigrateFail(const SocketAddr& dst); void OnConnect(QMigrateClient* ); private: QMigrationManager() { } std::unordered_map > items_; std::unordered_map > conns_; std::unordered_set pendingConnect_; }; class QMigrateClient : public StreamSocket { public: void OnConnect() override; private: PacketLength _HandlePacket(const char* msg, std::size_t len) override; size_t readyRsp_ = 0; }; } // end namespace qedis #endif ================================================ FILE: QedisCore/QModule.cc ================================================ #include #include #include #include "QCommon.h" #include "QModule.h" namespace qedis { QModule::QModule() : handler_(nullptr) { } QModule::~QModule() { UnLoad(); } const QString& QModule::Name() const { return soname_; } void QModule::Load(const char* so, bool lazy) throw(std::runtime_error) { assert (!handler_); ::dlerror(); // clear errors handler_ = ::dlopen(so, lazy ? RTLD_LAZY : RTLD_NOW); if (!handler_) { // try local path std::string localName("./"); localName += so; handler_ = ::dlopen(localName.c_str(), lazy ? RTLD_LAZY : RTLD_NOW); if (!handler_) { std::ostringstream oss; oss << "open [" << so << "] failed because:" << ::dlerror(); throw std::runtime_error(oss.str()); } } } void QModule::UnLoad() { if (handler_) { ::dlclose(handler_); handler_ = nullptr; } } void* QModule::Symbol(const char* symbol) { if (handler_) { ::dlerror(); return ::dlsym(handler_, symbol); } return nullptr; } QModuleManager& QModuleManager::Instance() { static QModuleManager mgr; return mgr; } QModule* QModuleManager::Load(const char* so, bool lazy) throw(std::logic_error, std::runtime_error) { auto module = std::make_shared(); module->Load(so, lazy); if (!modules_.insert({QString(so), module}).second) throw ModuleExist(so); // try call init function using InitFunc = bool (*)(); InitFunc initmodule = (InitFunc)(module->Symbol("QedisModule_OnLoad")); if (!initmodule || !initmodule()) { module->UnLoad(); modules_.erase(so); throw ModuleNoLoad(so); } return module.get(); } void QModuleManager::UnLoad(const char* so) { auto it = modules_.find(so); if (it == modules_.end()) throw ModuleNotExist(so); QEDIS_DEFER { it->second->UnLoad(); modules_.erase(it); }; // try call uninit function using UnInitFunc = void (*)(); UnInitFunc uninit = (UnInitFunc)(it->second->Symbol("QedisModule_OnUnLoad")); if (uninit) { uninit(); } else { throw ModuleNoUnLoad(so); } } QModule* QModuleManager::GetModule(const char* so) { auto it = modules_.find(so); if (it == modules_.end()) return nullptr; return it->second.get(); } std::vector QModuleManager::NameList() const { std::vector namelist; for (const auto& kv : modules_) { namelist.push_back(kv.first); } return namelist; } // MODULE LOAD /path/to/mymodule.so // MODULE LIST // MODULE UNLOAD mymodule QError module(const std::vector& params, UnboundedBuffer* reply) { // MODULE LOAD /path/to/mymodule.{so,dylib} if (strncasecmp(params[1].c_str(), "load", 4) == 0) { if (params.size() != 3) { ReplyError(QError_syntax, reply); return QError_syntax; } try { MODULES.Load(params[2].c_str()); } catch (const ModuleNoLoad& e) { ReplyError(QError_moduleinit, reply); return QError_moduleinit; } catch (const ModuleExist& e) { ReplyError(QError_modulerepeat, reply); return QError_modulerepeat; } catch (...) { ReplyError(QError_nomodule, reply); return QError_nomodule; } } // MODULE LIST else if (strncasecmp(params[1].c_str(), "list", 4) == 0) { auto names = MODULES.NameList(); PreFormatMultiBulk(names.size(), reply); for (const auto& name : names) { FormatBulk(name, reply); } return QError_ok; } // MODULE UNLOAD mymodule else if (strncasecmp(params[1].c_str(), "unload", 6) == 0) { if (params.size() != 3) { ReplyError(QError_syntax, reply); return QError_syntax; } try { MODULES.UnLoad(params[2].c_str()); } catch (const ModuleNotExist& ) { ReplyError(QError_nomodule, reply); return QError_nomodule; } catch (const ModuleNoUnLoad& ) { ReplyError(QError_moduleuninit, reply); return QError_moduleuninit; } } else { ReplyError(QError_syntax, reply); return QError_syntax; } FormatOK(reply); return QError_ok; } } ================================================ FILE: QedisCore/QModule.h ================================================ #ifndef BERT_QMODULE_H #define BERT_QMODULE_H #include #include #include #include #include "QString.h" namespace qedis { class ModuleExist : public std::logic_error { public: explicit ModuleExist(const QString& what) : std::logic_error(what) { } }; class ModuleNotExist : public std::logic_error { public: explicit ModuleNotExist(const QString& what) : std::logic_error(what) { } }; class ModuleNoLoad : public std::logic_error { public: explicit ModuleNoLoad(const QString& what) : std::logic_error(what) { } }; class ModuleNoUnLoad : public std::logic_error { public: explicit ModuleNoUnLoad(const QString& what) : std::logic_error(what) { } }; class QModule { public: QModule(); ~QModule(); const QString& Name() const; void Load(const char* so, bool lazy = false) throw(std::runtime_error); void UnLoad(); void* Symbol(const char* symbol); private: QString soname_; void* handler_; }; class QModuleManager { public: static QModuleManager& Instance(); QModuleManager(const QModuleManager& ) = delete; void operator= (const QModuleManager& ) = delete; QModule* Load(const char* so, bool lazy = false) throw(std::logic_error, std::runtime_error); void UnLoad(const char* so); QModule* GetModule(const char* so); std::vector NameList() const; private: QModuleManager() {} std::unordered_map > modules_; }; #define MODULES ::qedis::QModuleManager::Instance() } #endif ================================================ FILE: QedisCore/QMulti.cc ================================================ #include "QMulti.h" #include "QClient.h" #include "QStore.h" #include "Log/Logger.h" namespace qedis { QMulti& QMulti::Instance() { static QMulti mt; return mt; } void QMulti::Watch(QClient* client, int dbno, const QString& key) { if (client->Watch(dbno, key)) { Clients& cls = clients_[dbno][key]; cls.push_back(std::static_pointer_cast(client->shared_from_this())); } } bool QMulti::Multi(QClient* client) { if (client->IsFlagOn(ClientFlag_multi)) return false; client->ClearMulti(); client->SetFlag(ClientFlag_multi); return true; } bool QMulti::Exec(QClient* client) { return client->Exec(); } void QMulti::Discard(QClient* client) { client->ClearMulti(); client->ClearWatch(); } void QMulti::NotifyDirty(int dbno, const QString& key) { auto tmpDbIter = clients_.find(dbno); if (tmpDbIter == clients_.end()) return; auto& dbWatchedKeys = tmpDbIter->second; auto it = dbWatchedKeys.find(key); if (it == dbWatchedKeys.end()) return; Clients& cls = it->second; for (auto itCli(cls.begin()); itCli != cls.end(); ) { auto client(itCli->lock()); if (!client) { WRN << "Erase not exist client when notify dirty key[" << key << "]"; itCli = cls.erase(itCli); } else { if (client.get() != QClient::Current() && client->NotifyDirty(dbno, key)) { WRN << "Erase dirty client " << client->GetName() << " when notify dirty key[" << key << "]"; itCli = cls.erase(itCli); } else { ++ itCli; } } } if (cls.empty()) { dbWatchedKeys.erase(it); } } void QMulti::NotifyDirtyAll(int dbno) { if (dbno == -1) { for (auto& db_set : clients_) { for (auto& key_clients : db_set.second) { std::for_each(key_clients.second.begin(), key_clients.second.end(), [&] (const std::weak_ptr& wcli) { auto scli = wcli.lock(); if (scli) scli->SetFlag(ClientFlag_dirty); } ); } } } else { auto it = clients_.find(dbno); if (it != clients_.end()) { for (auto& key_clients : it->second) { std::for_each(key_clients.second.begin(), key_clients.second.end(), [&] (const std::weak_ptr& wcli) { auto scli = wcli.lock(); if (scli) scli->SetFlag(ClientFlag_dirty); } ); } } } } // multi commands QError watch(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (client->IsFlagOn(ClientFlag_multi)) { ReplyError(QError_watch, reply); return QError_watch; } std::for_each(++ params.begin(), params.end(), [client](const QString& s) { QMulti::Instance().Watch(client, QSTORE.GetDB(), s); } ); FormatOK(reply); return QError_ok; } QError unwatch(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); client->ClearWatch(); FormatOK(reply); return QError_ok; } QError multi(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (QMulti::Instance().Multi(client)) FormatOK(reply); else reply->PushData("-ERR MULTI calls can not be nested\r\n", sizeof "-ERR MULTI calls can not be nested\r\n" - 1); return QError_ok; } QError exec(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (!client->IsFlagOn(ClientFlag_multi)) { ReplyError(QError_noMulti, reply); return QError_noMulti; } if (!QMulti::Instance().Exec(client)) { ReplyError(QError_dirtyExec, reply); return QError_dirtyExec; } return QError_ok; } QError discard(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (!client->IsFlagOn(ClientFlag_multi)) { reply->PushData("-ERR DISCARD without MULTI\r\n", sizeof "-ERR DISCARD without MULTI\r\n" - 1); } else { QMulti::Instance().Discard(client); FormatOK(reply); } return QError_ok; } } ================================================ FILE: QedisCore/QMulti.h ================================================ #ifndef BERT_QMULTI_H #define BERT_QMULTI_H #include #include #include #include "QString.h" namespace qedis { class QClient; class QMulti { public: static QMulti& Instance(); QMulti(const QMulti& ) = delete; void operator= (const QMulti& ) = delete; void Watch(QClient* client, int dbno, const QString& key); bool Multi(QClient* client); bool Exec(QClient* client); void Discard(QClient* client); void NotifyDirty(int dbno, const QString& key); void NotifyDirtyAll(int dbno); private: QMulti() {} using Clients = std::vector >; using WatchedClients = std::map >; WatchedClients clients_; }; } #endif ================================================ FILE: QedisCore/QProtoParser.cc ================================================ #include "QCommon.h" #include "QProtoParser.h" #include // 1 request -> multi strlist // 2 multi -> * number crlf // 3 strlist -> str strlist | empty // 4 str -> strlen strval // 5 strlen -> $ number crlf // 6 strval -> string crlf namespace qedis { void QProtoParser::Reset() { multi_ = -1; paramLen_ = -1; numOfParam_ = 0; // Optimize: Most redis command has 3 args while (params_.size() > 3) params_.pop_back(); } QParseResult QProtoParser::ParseRequest(const char*& ptr, const char* end) { if (multi_ == -1) { auto parseRet = _ParseMulti(ptr, end, multi_); if (parseRet == QParseResult::error || multi_ < -1) return QParseResult::error; if (parseRet != QParseResult::ok) return QParseResult::wait; } return _ParseStrlist(ptr, end, params_); } QParseResult QProtoParser::_ParseMulti(const char*& ptr, const char* end, int& result) { if (end - ptr < 3) return QParseResult::wait; if (*ptr != '*') return QParseResult::error; ++ ptr; return GetIntUntilCRLF(ptr, end - ptr, result); } QParseResult QProtoParser::_ParseStrlist(const char*& ptr, const char* end, std::vector& results) { while (static_cast(numOfParam_) < multi_) { if (results.size() < numOfParam_ + 1) results.resize(numOfParam_ + 1); auto parseRet = _ParseStr(ptr, end, results[numOfParam_]); if (parseRet == QParseResult::ok) { ++ numOfParam_; } else { return parseRet; } } results.resize(numOfParam_); return QParseResult::ok; } QParseResult QProtoParser::_ParseStr(const char*& ptr, const char* end, QString& result) { if (paramLen_ == -1) { auto parseRet = _ParseStrlen(ptr, end, paramLen_); if (parseRet == QParseResult::error || paramLen_ < -1) return QParseResult::error; if (parseRet != QParseResult::ok) return QParseResult::wait; } if (paramLen_ == -1) { result.clear(); // or should be "(nil)" ? return QParseResult::ok; } else { return _ParseStrval(ptr, end, result); } } QParseResult QProtoParser::_ParseStrval(const char*& ptr, const char* end, QString& result) { assert (paramLen_ >= 0); if (static_cast(end - ptr) < paramLen_ + 2) return QParseResult::wait; auto tail = ptr + paramLen_; if (tail[0] != '\r' || tail[1] != '\n') return QParseResult::error; result.assign(ptr, tail - ptr); ptr = tail + 2; paramLen_ = -1; return QParseResult::ok; } QParseResult QProtoParser::_ParseStrlen(const char*& ptr, const char* end, int& result) { if (end - ptr < 3) return QParseResult::wait; if (*ptr != '$') return QParseResult::error; ++ ptr; const auto ret = GetIntUntilCRLF(ptr, end - ptr, result); if (ret != QParseResult::ok) -- ptr; return ret; } } ================================================ FILE: QedisCore/QProtoParser.h ================================================ #ifndef BERT_QPROTOPARSER_H #define BERT_QPROTOPARSER_H #include #include "QString.h" namespace qedis { class QProtoParser { public: void Reset(); QParseResult ParseRequest(const char*& ptr, const char* end); const std::vector& GetParams() const { return params_; } void SetParams(std::vector p) { params_ = std::move(p); } bool IsInitialState() const { return multi_ == -1; } private: QParseResult _ParseMulti(const char*& ptr, const char* end, int& result); QParseResult _ParseStrlist(const char*& ptr, const char* end, std::vector& results); QParseResult _ParseStr(const char*& ptr, const char* end, QString& result); QParseResult _ParseStrval(const char*& ptr, const char* end, QString& result); QParseResult _ParseStrlen(const char*& ptr, const char* end, int& result); int multi_ = -1; int paramLen_ = -1; size_t numOfParam_ = 0; // for optimize std::vector params_; }; } #endif ================================================ FILE: QedisCore/QPubsub.cc ================================================ #include "QPubsub.h" #include "QClient.h" #include "Log/Logger.h" #include "Timer.h" #include "QGlobRegex.h" namespace qedis { QPubsub& QPubsub::Instance() { static QPubsub ps; return ps; } size_t QPubsub::Subscribe(QClient* client, const QString& channel) { if (client && client->Subscribe(channel)) { auto it(channels_.find(channel)); if (it == channels_.end()) it = channels_.insert(ChannelClients::value_type(channel, Clients())).first; assert (it != channels_.end()); auto c = std::static_pointer_cast(client->shared_from_this()); bool succ = it->second.insert(std::move(c)).second; assert (succ); return 1; } return 0; } std::size_t QPubsub::UnSubscribe(QClient* client, const QString& channel) { if (client && client->UnSubscribe(channel)) { auto it(channels_.find(channel)); assert (it != channels_.end()); Clients& clientSet = it->second; auto c = std::static_pointer_cast(client->shared_from_this()); std::size_t n = clientSet.erase(c); assert (n == 1); if (clientSet.empty()) channels_.erase(it); return client->ChannelCount(); } return 0; } std::size_t QPubsub::UnSubscribeAll(QClient* client) { if (!client) return 0; std::size_t n = 0; const auto& channels = client->GetChannels(); for (const auto& channel : channels) { n += UnSubscribe(client, channel); } return n; } size_t QPubsub::PSubscribe(QClient* client, const QString& channel) { if (client && client->PSubscribe(channel)) { auto it(patternChannels_.find(channel)); if (it == patternChannels_.end()) it = patternChannels_.insert({channel, Clients()}).first; assert (it != patternChannels_.end()); auto c = std::static_pointer_cast(client->shared_from_this()); bool succ = it->second.insert(c).second; assert (succ); return 1; } return 0; } std::size_t QPubsub::PUnSubscribe(QClient* client, const QString& channel) { if (client && client->PUnSubscribe(channel)) { auto it(patternChannels_.find(channel)); assert (it != patternChannels_.end()); Clients& clientSet = it->second; auto c = std::static_pointer_cast(client->shared_from_this()); std::size_t n = clientSet.erase(c); assert (n == 1); if (clientSet.empty()) patternChannels_.erase(it); return client->PatternChannelCount(); } return 0; } std::size_t QPubsub::PUnSubscribeAll(QClient* client) { if (!client) return 0; std::size_t n = 0; const auto& channels = client->GetPatternChannels(); for (const auto& channel : channels) { n += PUnSubscribe(client, channel); } return n; } size_t QPubsub::_Publish(QPubsub::Clients& clients, const std::vector& args) { size_t n = 0; for (auto itCli(clients.begin()); itCli != clients.end(); ) { auto cli = itCli->lock(); if (!cli) { itCli = clients.erase(itCli); } else { SocketAddr peer; Socket::GetPeerAddr(cli->GetSocket(), peer); INF << "Publish msg:" << args.back() << " to " << peer.ToString(); UnboundedBuffer reply; PreFormatMultiBulk(args.size(), &reply); for (const auto& arg : args) { FormatBulk(arg, &reply); } cli->SendPacket(reply); ++ itCli; ++ n; } } return n; } std::size_t QPubsub::PublishMsg(const QString& channel, const QString& msg) { std::size_t n = 0; auto it(channels_.find(channel)); if (it != channels_.end()) { n += _Publish(it->second, {"message", channel, msg}); } for (auto& pattern : patternChannels_) { if (glob_match(pattern.first, channel)) { n += _Publish(pattern.second, {"pmessage", pattern.first, channel, msg}); INF << channel << " match " << pattern.first; } } return n; } void QPubsub::RecycleClients(QString& startChannel, QString& startPattern) { _RecycleClients(channels_, startChannel); _RecycleClients(patternChannels_, startPattern); } void QPubsub::_RecycleClients(ChannelClients& channels, QString& start) { auto it(start.empty() ? channels.begin() : channels.find(start)); if (it == channels.end()) it = channels.begin(); const size_t kEraseMax = 10; size_t n = 0; while (it != channels.end() && n < kEraseMax) { Clients& cls = it->second; for (auto itCli(cls.begin()); itCli != cls.end(); ) { if (itCli->expired()) { INF << "_RecycleClient on channel " << it->first; itCli = cls.erase(itCli); ++ n; } else { ++ itCli; } } if (cls.empty()) { INF << "erase channel " << it->first; it = channels.erase(it); } else { ++ it; } } if (it != channels.end()) start = it->first; else start.clear(); } void QPubsub::InitPubsubTimer() { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(200); timer->SetCallback([&] (std::string& channel, std::string& pattern) { QPubsub::Instance().RecycleClients(channel, pattern); }, std::ref(startChannel_), std::ref(startPattern_)); TimerManager::Instance().AddTimer(timer); } void QPubsub::PubsubChannels(std::vector& res, const char* pattern) const { res.clear(); for (const auto& elem : channels_) { if (!pattern || glob_match(pattern, elem.first)) { res.push_back(elem.first); } } } size_t QPubsub::PubsubNumsub(const QString& channel) const { auto it = channels_.find(channel); if (it != channels_.end()) return it->second.size(); return 0; } size_t QPubsub::PubsubNumpat() const { std::size_t n = 0; for (const auto& elem : patternChannels_) { n += elem.second.size(); } return n; } // pubsub commands QError subscribe(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); for (size_t i = 1; i < params.size(); ++ i) { const auto& pa = params[i]; size_t n = QPubsub::Instance().Subscribe(client, pa); if (n == 1) { PreFormatMultiBulk(3, reply); FormatBulk("subscribe", 9, reply); FormatBulk(pa, reply); FormatInt(client->ChannelCount(), reply); SocketAddr peer; Socket::GetPeerAddr(client->GetSocket(), peer); INF << "subscribe " << pa << " by " << peer.ToString(); } } return QError_ok; } QError psubscribe(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); for (size_t i = 1; i < params.size(); ++ i) { const auto& pa = params[i]; size_t n = QPubsub::Instance().PSubscribe(client, pa); if (n == 1) { PreFormatMultiBulk(3, reply); FormatBulk("psubscribe", 9, reply); FormatBulk(pa, reply); FormatInt(client->PatternChannelCount(), reply); SocketAddr peer; Socket::GetPeerAddr(client->GetSocket(), peer); INF << "psubscribe " << pa << " by " << peer.ToString(); } } return QError_ok; } QError unsubscribe(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (params.size() == 1) { const auto& channels = client->GetChannels(); for (const auto& channel : channels) { FormatBulk(channel, reply); } QPubsub::Instance().UnSubscribeAll(client); } else { std::set channels; for (size_t i = 1; i < params.size(); ++ i) { size_t n = QPubsub::Instance().UnSubscribe(client, params[i]); if (n == 1) { channels.insert(params[i]); SocketAddr peer; Socket::GetPeerAddr(client->GetSocket(), peer); INF << "unsubscribe " << params[i] << " by " << peer.ToString(); } } PreFormatMultiBulk(channels.size(), reply); for (const auto& channel : channels) { FormatBulk(channel, reply); } } return QError_ok; } QError punsubscribe(const std::vector& params, UnboundedBuffer* reply) { QClient* client = QClient::Current(); if (params.size() == 1) { const auto& channels = client->GetPatternChannels(); for (const auto& channel : channels) { FormatBulk(channel, reply); } QPubsub::Instance().PUnSubscribeAll(client); } else { std::set channels; for (size_t i = 1; i < params.size(); ++ i) { size_t n = QPubsub::Instance().PUnSubscribe(client, params[i]); if (n == 1) { channels.insert(params[i]); SocketAddr peer; Socket::GetPeerAddr(client->GetSocket(), peer); INF << "punsubscribe " << params[i] << " by " << peer.ToString(); } } PreFormatMultiBulk(channels.size(), reply); for (const auto& channel : channels) { FormatBulk(channel, reply); } } return QError_ok; } QError publish(const std::vector& params, UnboundedBuffer* reply) { size_t n = QPubsub::Instance().PublishMsg(params[1], params[2]); FormatInt(n, reply); return QError_ok; } // neixing command QError pubsub(const std::vector& params, UnboundedBuffer* reply) { if (params[1] == "channels") { if (params.size() > 3) { ReplyError(QError_param, reply); return QError_param; } std::vector res; QPubsub::Instance().PubsubChannels(res, params.size() == 3 ? params[2].c_str() : 0); PreFormatMultiBulk(res.size(), reply); for (const auto& channel : res) { FormatBulk(channel, reply); } } else if (params[1] == "numsub") { PreFormatMultiBulk(2 * (params.size() - 2), reply); for (size_t i = 2; i < params.size(); ++ i) { size_t n = QPubsub::Instance().PubsubNumsub(params[i]); FormatBulk(params[i], reply); FormatInt(n, reply); } } else if (params[1] == "numpat") { if (params.size() != 2) { ReplyError(QError_param, reply); return QError_param; } FormatInt(QPubsub::Instance().PubsubNumpat(), reply); } else { ERR << "Unknown pubsub subcmd " << params[1].c_str(); } return QError_ok; } } ================================================ FILE: QedisCore/QPubsub.h ================================================ #ifndef BERT_QPUBSUB_H #define BERT_QPUBSUB_H #include #include #include #include "QString.h" #include namespace qedis { class QClient; class QPubsub { public: static QPubsub& Instance(); QPubsub(const QPubsub& ) = delete; void operator= (const QPubsub& ) = delete; std::size_t Subscribe(QClient* client, const QString& channel); std::size_t UnSubscribe(QClient* client, const QString& channel); std::size_t UnSubscribeAll(QClient* client); std::size_t PublishMsg(const QString& channel, const QString& msg); std::size_t PSubscribe(QClient* client, const QString& pchannel); std::size_t PUnSubscribeAll(QClient* client); std::size_t PUnSubscribe(QClient* client, const QString& pchannel); // introspect void PubsubChannels(std::vector& res, const char* pattern = 0) const; std::size_t PubsubNumsub(const QString& channel) const; std::size_t PubsubNumpat() const; void InitPubsubTimer(); void RecycleClients(QString& startChannel, QString& startPattern); private: QPubsub() {} using Clients = std::set, std::owner_less > >; using ChannelClients = std::map; ChannelClients channels_; ChannelClients patternChannels_; QString startChannel_; QString startPattern_; static void _RecycleClients(ChannelClients& channels, QString& start); size_t _Publish(QPubsub::Clients& clients, const std::vector& args); }; } #endif ================================================ FILE: QedisCore/QReplication.cc ================================================ #include #include // the child process use stdout for log #include #include "Log/Logger.h" #include "QClient.h" #include "QConfig.h" #include "QCommon.h" #include "QDB.h" #include "QReplication.h" #include "QAOF.h" #include "Server.h" namespace qedis { QReplication& QReplication::Instance() { static QReplication rep; return rep; } QReplication::QReplication() { } bool QReplication::IsBgsaving() const { return bgsaving_; } void QReplication::AddSlave(qedis::QClient* cli) { slaves_.push_back(std::static_pointer_cast(cli->shared_from_this())); } bool QReplication::HasAnyWaitingBgsave() const { for (const auto& c : slaves_) { auto cli = c.lock(); if (cli && cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_start) { return true; } } return false; } void QReplication::OnRdbSaveDone() { bgsaving_ = false; InputMemoryFile rdb; // send rdb to slaves that wait rdb end, set state for (auto& wptr : slaves_) { auto cli = wptr.lock(); if (!cli) continue; if (cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_end) { cli->GetSlaveInfo()->state = QSlaveState_online; if (!rdb.IsOpen() && !rdb.Open(g_config.rdbfullname.c_str())) { ERR << "can not open rdb when replication\n"; return; // fatal error; } std::size_t size = std::size_t(-1); const char* data = rdb.Read(size); // $file_len + filedata char tmp[32]; int n = snprintf(tmp, sizeof tmp - 1, "$%ld\r\n", (long)size); cli->SendPacket(tmp, n); cli->SendPacket(data, size); cli->SendPacket(buffer_); INF << "Send to slave rdb " << size << ", buffer " << buffer_.ReadableSize(); } } buffer_.Clear(); } void QReplication::TryBgsave() { if (IsBgsaving()) return; if (!HasAnyWaitingBgsave()) return; int ret = fork(); if (ret == 0) { { QDBSaver qdb; qdb.Save(g_config.rdbfullname.c_str()); std::cerr << "QReplication save rdb done, exiting child\n"; } _exit(0); } else if (ret == -1) { ERR << "QReplication save rdb FATAL ERROR"; _OnStartBgsave(false); } else { INF << "QReplication save rdb START"; g_qdbPid = ret; _OnStartBgsave(true); } } void QReplication::_OnStartBgsave(bool succ) { buffer_.Clear(); bgsaving_ = succ; for (auto& c : slaves_) { auto cli = c.lock(); if (!cli) continue; if (cli->GetSlaveInfo()->state == QSlaveState_wait_bgsave_start) { if (succ) { INF << "_OnStartBgsave set cli wait bgsave end " << cli->GetName(); cli->GetSlaveInfo()->state = QSlaveState_wait_bgsave_end; } else { cli->OnError(); // release slave } } } } void QReplication::SendToSlaves(const std::vector& params) { if (IsBgsaving()) { // 在执行rdb期间,缓存变化 SaveCommand(params, buffer_); return; } UnboundedBuffer ub; for (const auto& wptr : slaves_) { auto cli = wptr.lock(); if (!cli || cli->GetSlaveInfo()->state != QSlaveState_online) continue; if (ub.IsEmpty()) SaveCommand(params, ub); cli->SendPacket(ub); } } void QReplication::Cron() { static unsigned pingCron = 0; if (pingCron ++ % 50 == 0) { for (auto it = slaves_.begin(); it != slaves_.end(); ) { auto cli = it->lock(); if (!cli) { it = slaves_.erase(it); } else { ++ it; if (cli->GetSlaveInfo()->state == QSlaveState_online) cli->SendPacket("PING\r\n", 6); } } } if (!masterInfo_.addr.Empty()) { switch (masterInfo_.state) { case QReplState_none: { SocketAddr localSvr(g_config.ip.c_str() , g_config.port); if (masterInfo_.addr.GetAddr() == localSvr) { ERR << "Fix config, master addr is self addr!"; assert(!!!"wrong config for master addr"); } if (auto master = master_.lock()) { WRN << "Disconnect from previous master " << master->GetPeerAddr().GetIP(); master->OnError(); } INF << "Try connect to master " << masterInfo_.addr.GetIP() << ":" << masterInfo_.addr.GetPort(); Server::Instance()->TCPConnect(masterInfo_.addr, [&]() { WRN << "OnCallback: Connect master failed"; QREPL.SetMasterState(QReplState_none); if (!masterInfo_.downSince) masterInfo_.downSince = ::time(nullptr); }, ConnectionTag::kQedisClient); masterInfo_.state = QReplState_connecting; } break; case QReplState_connected: if (!g_config.masterauth.empty()) { if (auto master = master_.lock()) { UnboundedBuffer req; req.PushData("auth ", 5); req.PushData(g_config.masterauth.data(), g_config.masterauth.size()); req.PushData("\r\n", 2); master->SendPacket(req); INF << "send auth with password " << g_config.masterauth; masterInfo_.state = QReplState_wait_auth; break; } } // fall through to next case. case QReplState_wait_auth: { auto master = master_.lock(); if (!master) { masterInfo_.state = QReplState_none; masterInfo_.downSince = ::time(nullptr); WRN << "Master is down from wait_auth to none"; } else if (master->GetAuth()) { // send replconf char req[128]; auto len = snprintf(req, sizeof req - 1, "replconf listening-port %hu\r\n", g_config.port); master->SendPacket(req, len); masterInfo_.state = QReplState_wait_replconf; INF << "Send replconf listening-port " << g_config.port; } else { WRN << "Haven't auth to master yet, or check masterauth password"; } } break; case QReplState_wait_replconf: { auto master = master_.lock(); if (!master) { masterInfo_.state = QReplState_none; masterInfo_.downSince = ::time(nullptr); WRN << "Master is down from wait_replconf to none"; } else { // request sync rdb file master->SendPacket("SYNC\r\n", 6); INF << "Request SYNC"; rdb_.Open(slaveRdbFile, false); masterInfo_.rdbRecved = 0; masterInfo_.rdbSize = std::size_t(-1); masterInfo_.state = QReplState_wait_rdb;; } } break; case QReplState_wait_rdb: break; case QReplState_online: if (auto master = master_.lock()) { } else { masterInfo_.state = QReplState_none; masterInfo_.downSince = ::time(nullptr); WRN << "Master is down"; } break; default: break; } } else { if (masterInfo_.state != QReplState_none) { auto master = master_.lock(); if (master) { SocketAddr peer; Socket::GetPeerAddr(master->GetSocket(), peer); INF << master->GetName() << " disconnect with Master " << peer.GetIP(); master->SetOnDisconnect(); master->OnError(); } masterInfo_.state = QReplState_none; masterInfo_.downSince = ::time(nullptr); WRN << "Master is down from connected to none"; } } } void QReplication::SaveTmpRdb(const char* data, std::size_t& len) { if (masterInfo_.rdbRecved + len > masterInfo_.rdbSize) len = masterInfo_.rdbSize - masterInfo_.rdbRecved; rdb_.Write(data, len); masterInfo_.rdbRecved += len; if (masterInfo_.rdbRecved == masterInfo_.rdbSize) { INF << "Rdb recv complete, bytes " << masterInfo_.rdbSize; QSTORE.ResetDb(); QDBLoader loader; loader.Load(slaveRdbFile); masterInfo_.state = QReplState_online; masterInfo_.downSince = 0; } } void QReplication::SetMaster(const std::shared_ptr& cli) { master_ = cli; } void QReplication::SetMasterState(QReplState s) { masterInfo_.state = s; } QReplState QReplication::GetMasterState() const { return masterInfo_.state; } SocketAddr QReplication::GetMasterAddr() const { return masterInfo_.addr; } void QReplication::SetMasterAddr(const char* ip, unsigned short port) { if (ip) masterInfo_.addr.Init(ip, port); else masterInfo_.addr.Clear(); } void QReplication::SetRdbSize(std::size_t s) { masterInfo_.rdbSize = s; } std::size_t QReplication::GetRdbSize() const { return masterInfo_.rdbSize; } QError replconf(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 == 0) { ReplyError(QError_syntax, reply); return QError_syntax; } for (size_t i = 1; i < params.size(); i += 2) { if (strncasecmp(params[i].c_str(), "listening-port", 14) == 0) { long port; if (!TryStr2Long(params[i + 1].c_str(), params[i + 1].size(), port)) { ReplyError(QError_param, reply); return QError_param; } auto info = QClient::Current()->GetSlaveInfo(); if (!info) { QClient::Current()->SetSlaveInfo(); info = QClient::Current()->GetSlaveInfo(); QREPL.AddSlave(QClient::Current()); } info->listenPort = static_cast(port); } else { if (reply) { reply->PushData("-ERR:Unrecognized REPLCONF option:", sizeof "-ERR:Unrecognized REPLCONF option:" - 1); reply->PushData(params[i].data(), params[i].size()); } return QError_syntax; } } FormatOK(reply); return QError_ok; } void QReplication::OnInfoCommand(UnboundedBuffer& res) { const char* slaveState[] = { "none", "wait_bgsave", "wait_bgsave", //"send_bulk", // qedis does not have send bulk state "online", }; std::ostringstream oss; int index = 0; for (const auto& c : slaves_) { auto cli = c.lock(); if (cli) { oss << "slave" << index << ":"; index ++; char tmpIp[32] = {}; cli->GetPeerAddr().GetIP(tmpIp, (socklen_t)(sizeof tmpIp)); oss << tmpIp; auto slaveInfo = cli->GetSlaveInfo(); auto state = slaveInfo ? slaveInfo->state : 0; oss << "," << (slaveInfo ? slaveInfo->listenPort : 0) << "," << slaveState[state] << "\r\n"; } } std::string slaveInfo(oss.str()); char buf[1024] = {}; bool isMaster = GetMasterAddr().Empty(); int n = snprintf(buf, sizeof buf - 1, "# Replication\r\n" "role:%s\r\n" "connected_slaves:%d\r\n%s" , isMaster ? "master" : "slave" , index , slaveInfo.c_str()); std::ostringstream masterInfo; if (!isMaster) { char tmpIp[32] = {}; masterInfo_.addr.GetIP(tmpIp, (socklen_t)(sizeof tmpIp)); masterInfo << "master_host:" << tmpIp << "\r\nmaster_port:" << masterInfo_.addr.GetPort() << "\r\nmaster_link_status:"; auto master = master_.lock(); masterInfo << (master ? "up\r\n" : "down\r\n"); if (!master) { if (!masterInfo_.downSince) assert (0); masterInfo << "master_link_down_since_seconds:" << (::time(nullptr) - masterInfo_.downSince) << "\r\n"; } } if (!res.IsEmpty()) res.PushData("\r\n", 2); res.PushData(buf, n); { std::string info(masterInfo.str()); res.PushData(info.c_str(), info.size()); } } QError slaveof(const std::vector& params, UnboundedBuffer* reply) { if (strncasecmp(params[1].data(), "no", 2) == 0 && strncasecmp(params[2].data(), "one", 3) == 0) { QREPL.SetMasterAddr(nullptr, 0); } else { unsigned short port = static_cast(std::stoi(params[2])); SocketAddr reqMaster(params[1].c_str(), port); if (port > 0 && QREPL.GetMasterAddr() != reqMaster) { QREPL.SetMasterAddr(params[1].c_str(), port); QREPL.SetMasterState(QReplState_none); } } FormatOK(reply); return QError_ok; } QError sync(const std::vector& params, UnboundedBuffer* reply) { QClient* cli = QClient::Current(); auto slave = cli->GetSlaveInfo(); if (!slave) { cli->SetSlaveInfo(); slave = cli->GetSlaveInfo(); QREPL.AddSlave(cli); } if (slave->state == QSlaveState_wait_bgsave_end || slave->state == QSlaveState_online) { WRN << cli->GetName() << " state is " << slave->state << ", ignore this sync request"; return QError_ok; } slave->state = QSlaveState_wait_bgsave_start; QREPL.TryBgsave(); return QError_ok; } } ================================================ FILE: QedisCore/QReplication.h ================================================ #ifndef BERT_QREPLICATION_H #define BERT_QREPLICATION_H #include #include #include "UnboundedBuffer.h" #include "Socket.h" #include "Log/MemoryFile.h" namespace qedis { // master side enum QSlaveState { QSlaveState_none, QSlaveState_wait_bgsave_start, // 有非sync的bgsave进行 要等待 QSlaveState_wait_bgsave_end, // sync bgsave正在进行 //QSlaveState_send_rdb, // 这个slave在接受rdb文件 QSlaveState_online, }; struct QSlaveInfo { QSlaveState state; unsigned short listenPort; // slave listening port QSlaveInfo() : state(QSlaveState_none), listenPort(0) { } }; // slave side enum QReplState { QReplState_none, QReplState_connecting, QReplState_connected, QReplState_wait_auth, // wait auth to be confirmed QReplState_wait_replconf, // wait replconf to be confirmed QReplState_wait_rdb, // wait to recv rdb file QReplState_online, }; struct QMasterInfo { SocketAddr addr; QReplState state; time_t downSince; // For recv rdb std::size_t rdbSize; std::size_t rdbRecved; QMasterInfo() { state = QReplState_none; downSince = 0; rdbSize = std::size_t(-1); rdbRecved = 0; } }; //tmp filename const char* const slaveRdbFile = "slave.rdb"; class QClient; class QReplication { public: static QReplication& Instance(); QReplication(const QReplication& ) = delete; void operator= (const QReplication& ) = delete; void Cron(); // master side bool IsBgsaving() const; bool HasAnyWaitingBgsave() const; void AddSlave(QClient* cli); void TryBgsave(); bool StartBgsave(); void OnStartBgsave(); void OnRdbSaveDone(); void SendToSlaves(const std::vector& params); // slave side void SaveTmpRdb(const char* data, std::size_t& len); void SetMaster(const std::shared_ptr& cli); void SetMasterState(QReplState s); void SetMasterAddr(const char* ip, unsigned short port); void SetRdbSize(std::size_t s); QReplState GetMasterState() const; SocketAddr GetMasterAddr() const; std::size_t GetRdbSize() const; // info command void OnInfoCommand(UnboundedBuffer& res); private: QReplication(); void _OnStartBgsave(bool succ); // master side bool bgsaving_; UnboundedBuffer buffer_; std::list > slaves_; //slave side QMasterInfo masterInfo_; std::weak_ptr master_; OutputMemoryFile rdb_; }; } #define QREPL qedis::QReplication::Instance() #endif ================================================ FILE: QedisCore/QServerCommand.cc ================================================ #include #include #include #include "QStore.h" #include "QClient.h" #include "Log/Logger.h" #include "Server.h" #include "QDB.h" #include "QAOF.h" #include "QConfig.h" #include "QSlowLog.h" #include "QGlobRegex.h" #include "Delegate.h" namespace qedis { QError select(const std::vector& params, UnboundedBuffer* reply) { int newDb = atoi(params[1].c_str()); auto client = QClient::Current(); if (client) { if (client->SelectDB(newDb)) FormatOK(reply); else ReplyError(QError_invalidDB, reply); } else { QSTORE.SelectDB(newDb); } return QError_ok; } QError dbsize(const std::vector& params, UnboundedBuffer* reply) { FormatInt(static_cast(QSTORE.DBSize()), reply); return QError_ok; } QError flushdb(const std::vector& params, UnboundedBuffer* reply) { QSTORE.dirty_ += QSTORE.DBSize(); QSTORE.ClearCurrentDB(); Propogate(QSTORE.GetDB(), params); FormatOK(reply); return QError_ok; } QError flushall(const std::vector& params, UnboundedBuffer* reply) { int currentDb = QSTORE.GetDB(); QEDIS_DEFER { QSTORE.SelectDB(currentDb); Propogate(-1, params); QSTORE.ResetDb(); }; for (int dbno = 0; true; ++ dbno) { if (QSTORE.SelectDB(dbno) == -1) break; QSTORE.dirty_ += QSTORE.DBSize(); } FormatOK(reply); return QError_ok; } QError bgsave(const std::vector& params, UnboundedBuffer* reply) { if (g_qdbPid != -1 || g_rewritePid != -1) { FormatBulk("-ERR Background save or aof already in progress", sizeof "-ERR Background save or aof already in progress" - 1, reply); return QError_ok; } int ret = fork(); if (ret == 0) { { QDBSaver qdb; qdb.Save(g_config.rdbfullname.c_str()); } _exit(0); } else if (ret == -1) { FormatSingle("Background saving FAILED", 24, reply); } else { g_qdbPid = ret; FormatSingle("Background saving started", 25, reply); } return QError_ok; } QError save(const std::vector& params, UnboundedBuffer* reply) { if (g_qdbPid != -1 || g_rewritePid != -1) { FormatBulk("-ERR Background save or aof already in progress", sizeof "-ERR Background save or aof already in progress" - 1, reply); return QError_ok; } QDBSaver qdb; qdb.Save(g_config.rdbfullname.c_str()); g_lastQDBSave = time(NULL); FormatOK(reply); return QError_ok; } QError lastsave(const std::vector& params, UnboundedBuffer* reply) { FormatInt(g_lastQDBSave, reply); return QError_ok; } QError client(const std::vector& params, UnboundedBuffer* reply) { // getname setname kill list QError err = QError_ok; if (params[1].size() == 7 && strncasecmp(params[1].c_str(), "getname", 7) == 0) { if (params.size() != 2) ReplyError(err = QError_param, reply); else FormatBulk(QClient::Current()->GetName(), reply); } else if (params[1].size() == 7 && strncasecmp(params[1].c_str(), "setname", 7) == 0) { if (params.size() != 3) { ReplyError(err = QError_param, reply); } else { QClient::Current()->SetName(params[2]); FormatOK(reply); } } else if (params[1].size() == 4 && strncasecmp(params[1].c_str(), "kill", 4) == 0) { // only kill current client //QClient::Current()->OnError(); FormatOK(reply); } else if (params[1].size() == 4 && strncasecmp(params[1].c_str(), "list", 4) == 0) { FormatOK(reply); } else { ReplyError(err = QError_param, reply); } return err; } static int Suicide() { int* ptr = nullptr; *ptr = 0; return *ptr; } QError debug(const std::vector& params, UnboundedBuffer* reply) { QError err = QError_ok; if (strncasecmp(params[1].c_str(), "segfault", 8) == 0 && params.size() == 2) { Suicide(); assert (false); } else if (strncasecmp(params[1].c_str(), "object", 6) == 0 && params.size() == 3) { QObject* obj = nullptr; err = QSTORE.GetValue(params[2], obj, false); if (err != QError_ok) { ReplyError(err, reply); } else { // ref count, encoding, idle time char buf[512]; int len = snprintf(buf, sizeof buf, "ref count:%ld, encoding:%s, idletime:%u", 1L, // TODO ? EncodingStringInfo(obj->encoding), EstimateIdleTime(obj->lru)); FormatBulk(buf, len, reply); } } else { ReplyError(err = QError_param, reply); } return err; } QError shutdown(const std::vector& params, UnboundedBuffer* reply) { if (params.size() == 2 && strncasecmp(params[1].c_str(), "save", 4) == 0) { QDBSaver qdb; qdb.Save(g_config.rdbfullname.c_str()); } Server::Instance()->Terminate(); return QError_ok; } QError ping(const std::vector& params, UnboundedBuffer* reply) { FormatSingle("PONG", 4, reply); return QError_ok; } QError echo(const std::vector& params, UnboundedBuffer* reply) { FormatBulk(params[1], reply); return QError_ok; } void OnMemoryInfoCollect(UnboundedBuffer& res) { // memory info auto minfo = getMemoryInfo(); char buf[1024]; int n = snprintf(buf, sizeof buf - 1, "# Memory\r\n" "used_memory_peak:%lu\r\n" "used_memory:%lu\r\n" "used_memory_human:%sMB\r\n" "used_memory_rss_peak:%lu\r\n" "used_memory_rss:%lu\r\n" "used_memory_rss_human:%sMB\r\n" "used_memory_lock:%lu\r\n" "used_memory_swap:%lu\r\n" , minfo[VmPeak] , minfo[VmSize] , std::to_string(minfo[VmSize] / 1024.0f / 1024.0f).data() , minfo[VmHWM] , minfo[VmRSS] , std::to_string(minfo[VmRSS] / 1024.0f / 1024.0f).data() , minfo[VmLck] , minfo[VmSwap] ); if (!res.IsEmpty()) res.PushData("\r\n", 2); res.PushData(buf, n); } void OnServerInfoCollect(UnboundedBuffer& res) { char buf[1024]; // server struct utsname name; uname(&name); int n = snprintf(buf, sizeof buf - 1, "# Server\r\n" "redis_mode:standalone\r\n" // not cluster node yet "os:%s %s %s\r\n" "run_id:%s\r\n" "hz:%d\r\n" "tcp_port:%hu\r\n" , name.sysname, name.release, name.machine , g_config.runid.data() , g_config.hz , g_config.port); if (!res.IsEmpty()) res.PushData("\r\n", 2); res.PushData(buf, n); } void OnClientInfoCollect(UnboundedBuffer& res) { char buf[1024]; int n = snprintf(buf, sizeof buf - 1, "# Clients\r\n" "connected_clients:%lu\r\n" "blocked_clients:%lu\r\n" , Server::Instance()->TCPSize() , QSTORE.BlockedSize()); if (!res.IsEmpty()) res.PushData("\r\n", 2); res.PushData(buf, n); } QError info(const std::vector& params, UnboundedBuffer* reply) { UnboundedBuffer res; extern Delegate g_infoCollector; g_infoCollector(res); FormatBulk(res.ReadAddr(), res.ReadableSize(), reply); return QError_ok; } QError monitor(const std::vector& params, UnboundedBuffer* reply) { QClient::AddCurrentToMonitor(); FormatOK(reply); return QError_ok; } QError auth(const std::vector& params, UnboundedBuffer* reply) { if (g_config.CheckPassword(params[1])) { QClient::Current()->SetAuth(); FormatOK(reply); } else { ReplyError(QError_errAuth, reply); } return QError_ok; } QError slowlog(const std::vector& params, UnboundedBuffer* reply) { if (params[1] == "len") { FormatInt(static_cast(QSlowLog::Instance().GetLogsCount()), reply); } else if (params[1] == "reset") { QSlowLog::Instance().ClearLogs(); FormatOK(reply); } else if (params[1] == "get") { const long limit = static_cast(QSlowLog::Instance().GetLogsCount()); long realCnt = limit; if (params.size() == 3) { if (!Strtol(params[2].c_str(), params[2].size(), &realCnt)) { ReplyError(QError_syntax, reply); return QError_syntax; } } if (realCnt > limit) realCnt = limit; PreFormatMultiBulk(realCnt, reply); for (const auto& item : QSlowLog::Instance().GetLogs()) { if (realCnt -- == 0) break; PreFormatMultiBulk(2, reply); FormatInt(static_cast(item.used), reply); PreFormatMultiBulk(static_cast(item.cmds.size()), reply); for (const auto& c : item.cmds) { FormatBulk(c, reply); } } } else { ReplyError(QError_syntax, reply); return QError_syntax; } return QError_ok; } // Config options get/set // enum ConfigType { Config_string, Config_bool, Config_int, Config_int64, }; struct ConfigInfo { ConfigType type; bool canModify; void* value; }; // TODO sanity check: use function setter std::map configOptions = { {"appendonly", {Config_bool, true, &g_config.appendonly }}, {"bind", {Config_string, false, &g_config.ip}}, {"dbfilename", {Config_string, true, &g_config.rdbfullname}}, {"databases", {Config_int, false, &g_config.databases}}, {"daemonize", {Config_bool, false, &g_config.daemonize}}, {"hz", {Config_int, false, &g_config.hz}}, {"logfile", {Config_string, false, &g_config.logdir}}, {"loglevel", {Config_string, true, &g_config.loglevel}}, {"masterauth", {Config_string, true, &g_config.masterauth}}, {"maxclients", {Config_int, true, &g_config.maxclients}}, {"port", {Config_int, false, &g_config.port}}, {"requirepass", {Config_string, true, &g_config.password}}, {"rdbchecksum", {Config_bool, false, &g_config.rdbchecksum}}, {"rdbcompression", {Config_bool, false, &g_config.rdbcompression}}, {"slowlog-log-slower-than", {Config_int, true, &g_config.slowlogtime}}, {"slowlog-max-len", {Config_int, true, &g_config.slowlogmaxlen}}, {"slaveof", {Config_string, false, &g_config.masterIp}}, {"maxmemory", {Config_int64, true, &g_config.maxmemory}}, {"maxmemorySamples", {Config_int, true, &g_config.maxmemorySamples}}, {"maxmemory-noevict", {Config_bool, true, &g_config.noeviction}}, {"backend", {Config_int, false, &g_config.backend}}, {"backendhz", {Config_int, false, &g_config.backendHz}}, }; static std::vector GetConfig(const QString& option) { std::vector res; std::vector::const_iterator> iters; if (NotGlobRegex(option.data(), option.size())) { auto it = configOptions.find(option); if (it == configOptions.end()) return res; iters.push_back(it); } else { // try glob match for (auto it(configOptions.begin()); it != configOptions.end(); ++ it) { if (glob_match(option, it->first)) iters.push_back(it); } } for (const auto& it : iters) { res.push_back(it->first); // push option // push value switch (it->second.type) { case Config_bool: if (*(bool*)(it->second.value)) res.push_back("true"); else res.push_back("false"); break; case Config_string: res.push_back(*(const QString*)it->second.value); break; case Config_int: case Config_int64: { int64_t val = 0; if (it->second.type == Config_int) val = *(int*)it->second.value; else val = *(int64_t*)it->second.value; char buf[16] = ""; Number2Str(buf, sizeof buf, val); res.push_back(buf); } break; default: assert(!!!"invalid type"); } } return res; } static QError SetConfig(const QString& option, const QString& value) { auto it = configOptions.find(option); if (it == configOptions.end()) return QError_syntax; if (!it->second.canModify) return QError_syntax; // set option value switch (it->second.type) { case Config_bool: *(bool*)(it->second.value) = (value == "true"); break; case Config_string: *(QString*)it->second.value = value; break; case Config_int: case Config_int64: { long val = 0; if (Strtol(value.data(), value.size(), &val)) { if (it->second.type == Config_int) *(int*)it->second.value = static_cast(val); else *(int64_t*)it->second.value = static_cast(val); // ugly... process slow log option if (option.find("slowlog") == 0) { QSlowLog::Instance().SetThreshold(g_config.slowlogtime); QSlowLog::Instance().SetLogLimit(static_cast(g_config.slowlogmaxlen)); } } else { return QError_syntax; } } break; default: assert(!!!"invalid type"); } return QError_ok; } QError config(const std::vector& params, UnboundedBuffer* reply) { // at least 3 params if (strncasecmp(params[1].c_str(), "get", 3) == 0) { auto res = GetConfig(params[2]); PreFormatMultiBulk(res.size(), reply); for (const auto& e : res) { FormatBulk(e, reply); } } else if (strncasecmp(params[1].c_str(), "set", 3) == 0) { if (params.size() != 4) { ReplyError(QError_param, reply); return QError_param; } auto err = SetConfig(params[2], params[3]); if (err == QError_ok) { FormatOK(reply); } else { const char* format = "-ERR Invalid argument '%s' for CONFIG SET '%s'\r\n"; char info[128]; auto len = snprintf(info, sizeof info, format, params[3].data(), params[2].data()); reply->PushData(info, len); } return err; } else { ReplyError(QError_syntax, reply); return QError_syntax; } return QError_ok; } } ================================================ FILE: QedisCore/QSet.cc ================================================ #include "QSet.h" #include "QStore.h" #include "QClient.h" #include namespace qedis { QObject QObject::CreateSet() { QObject set(QType_set); set.Reset(new QSet); return set; } #define GET_SET(setname) \ QObject* value; \ QError err = QSTORE.GetValueByType(setname, value, QType_set); \ if (err != QError_ok) { \ if (err == QError_notExist) \ FormatNull(reply); \ else \ ReplyError(err, reply); \ return err; \ } #define GET_OR_SET_SET(setname) \ QObject* value; \ QError err = QSTORE.GetValueByType(setname, value, QType_set); \ if (err != QError_ok && err != QError_notExist) { \ ReplyError(err, reply); \ return err; \ } \ if (err == QError_notExist) { \ value = QSTORE.SetValue(setname, QObject::CreateSet()); \ } static bool RandomMember(const QSet& set, QString& res) { QSet::const_local_iterator it = RandomHashMember(set); if (it != QSet::const_local_iterator()) { res = *it; return true; } return false; } QError spop(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); QString res; if (RandomMember(*set, res)) { FormatBulk(res, reply); set->erase(res); if (set->empty()) QSTORE.DeleteKey(params[1]); std::vector translated; translated.push_back("srem"); translated.push_back(params[1]); translated.push_back(res); QClient::Current()->RewriteCmd(translated); } else { FormatNull(reply); return QError_notExist; } return QError_ok; } QError srandmember(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); QString res; if (RandomMember(*set, res)) { FormatBulk(res, reply); } else { FormatNull(reply); } return QError_ok; } QError sadd(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_SET(params[1]); int res = 0; auto set = value->CastSet(); for (size_t i = 2; i < params.size(); ++ i) { if (set->insert(params[i]).second) ++ res; } FormatInt(res, reply); return QError_ok; } QError scard(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); long size = static_cast(set->size()); FormatInt(size, reply); return QError_ok; } QError srem(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); int res = 0; for (size_t i = 2; i < params.size(); ++ i) { if (set->erase(params[i]) != 0) ++ res; } if (set->empty()) QSTORE.DeleteKey(params[1]); FormatInt(res, reply); return QError_ok; } QError sismember(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); long res = static_cast(set->count(params[2])); FormatInt(res, reply); return QError_ok; } QError smembers(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); PreFormatMultiBulk(set->size(), reply); for (const auto& member : *set) FormatBulk(member, reply); return QError_ok; } QError smove(const std::vector& params, UnboundedBuffer* reply) { GET_SET(params[1]); auto set = value->CastSet(); int ret = static_cast(set->erase(params[3])); if (ret != 0) { QObject* dst; err = QSTORE.GetValueByType(params[2], dst, QType_set); if (err == QError_notExist) { err = QError_ok; QObject val(QObject::CreateSet()); dst = QSTORE.SetValue(params[2], std::move(val)); } if (err == QError_ok) { auto dset = dst->CastSet(); dset->insert(params[3]); } } FormatInt(ret, reply); return err; } QSet& QSet_diff(const QSet& l, const QSet& r, QSet& result) { for (const auto& le : l) { if (r.count(le) == 0) { result.insert(le); } } return result; } QSet& QSet_inter(const QSet& l, const QSet& r, QSet& result) { for (const auto& le : l) { if (r.count(le) != 0) { result.insert(le); } } return result; } QSet& QSet_union(const QSet& l, const QSet& r, QSet& result) { for (const auto& re : r) { result.insert(re); } for (const auto& le : l) { result.insert(le); } return result; } enum SetOperation { SetOperation_diff, SetOperation_inter, SetOperation_union, }; static void _set_operation(const std::vector& params, size_t offset, QSet& res, SetOperation oper) { QObject* value; QError err = QSTORE.GetValueByType(params[offset], value, QType_set); if (err != QError_ok && oper != SetOperation_union) return; auto set = value->CastSet(); if (set) res = *set; for (size_t i = offset + 1; i < params.size(); ++ i) { QObject* val; QError err = QSTORE.GetValueByType(params[i], val, QType_set); if (err != QError_ok) { if (oper == SetOperation_inter) { res.clear(); return; } continue; } QSet tmp; auto r = val->CastSet(); if (oper == SetOperation_diff) QSet_diff(res, *r, tmp); else if (oper == SetOperation_inter) QSet_inter(res, *r, tmp); else if (oper == SetOperation_union) QSet_union(res, *r, tmp); res.swap(tmp); if (oper != SetOperation_union && res.empty()) return; } } QError sdiffstore(const std::vector& params, UnboundedBuffer* reply) { QObject obj(QObject::CreateSet()); auto res = obj.CastSet(); QSTORE.SetValue(params[1], std::move(obj)); _set_operation(params, 2, *res, SetOperation_diff); FormatInt(static_cast(res->size()), reply); return QError_ok; } QError sdiff(const std::vector& params, UnboundedBuffer* reply) { QSet res; _set_operation(params, 1, res, SetOperation_diff); PreFormatMultiBulk(res.size(), reply); for (const auto& elem : res) FormatBulk(elem, reply); return QError_ok; } QError sinter(const std::vector& params, UnboundedBuffer* reply) { QSet res; _set_operation(params, 1, res, SetOperation_inter); PreFormatMultiBulk(res.size(), reply); for (const auto& elem : res) FormatBulk(elem, reply); return QError_ok; } QError sinterstore(const std::vector& params, UnboundedBuffer* reply) { QObject obj(QObject::CreateSet()); auto res = obj.CastSet(); QSTORE.SetValue(params[1], std::move(obj)); _set_operation(params, 2, *res, SetOperation_inter); FormatInt(static_cast(res->size()), reply); return QError_ok; } QError sunion(const std::vector& params, UnboundedBuffer* reply) { QSet res; _set_operation(params, 1, res, SetOperation_union); PreFormatMultiBulk(res.size(), reply); for (const auto& elem : res) FormatBulk(elem, reply); return QError_ok; } QError sunionstore(const std::vector& params, UnboundedBuffer* reply) { QObject obj(QObject::CreateSet()); auto res = obj.CastSet(); QSTORE.SetValue(params[1], std::move(obj)); _set_operation(params, 2, *res, SetOperation_union); FormatInt(static_cast(res->size()), reply); return QError_ok; } size_t SScanKey(const QSet& qset, size_t cursor, size_t count, std::vector& res) { if (qset.empty()) return 0; std::vector iters; size_t newCursor = ScanHashMember(qset, cursor, count, iters); res.reserve(iters.size()); for (auto it : iters) res.push_back(*it); return newCursor; } } ================================================ FILE: QedisCore/QSet.h ================================================ #ifndef BERT_QSET_H #define BERT_QSET_H #include "QHelper.h" #include namespace qedis { using QSet = std::unordered_set >; size_t SScanKey(const QSet& qset, size_t cursor, size_t count, std::vector& res); } #endif ================================================ FILE: QedisCore/QSlaveClient.cc ================================================ #include "QSlaveClient.h" #include "QConfig.h" #include "QCommon.h" #include "Log/Logger.h" namespace qedis { void QSlaveClient::OnConnect() { std::string cmd = BuildInlineRequest("slaveof ", g_config.ip, std::to_string(g_config.port)); INF << "Send to slave cmd " << cmd; SendPacket(cmd.data(), cmd.size()); auto wk = std::weak_ptr(std::static_pointer_cast(this->shared_from_this())); Timer* timer = TimerManager::Instance().CreateTimer(); timer->Init(3 * 1000, 1); timer->SetCallback([wk]() { auto me = wk.lock(); if (me) { USR << "OnTimer close " << me->GetPeerAddr().ToString(); me->OnError(); } }); TimerManager::Instance().AsyncAddTimer(timer); } PacketLength QSlaveClient::_HandlePacket(const char* msg, std::size_t len) { return static_cast(len); } } ================================================ FILE: QedisCore/QSlaveClient.h ================================================ #ifndef BERT_QSLAVECLIENT_H #define BERT_QSLAVECLIENT_H #include "StreamSocket.h" namespace qedis { class QSlaveClient : public StreamSocket { public: void OnConnect() override; private: PacketLength _HandlePacket(const char* msg, std::size_t len) override; }; } #endif ================================================ FILE: QedisCore/QSlowLog.cc ================================================ #include #include #include #include "Log/Logger.h" #include "QSlowLog.h" namespace qedis { QSlowLog& QSlowLog::Instance() { static QSlowLog slog; return slog; } QSlowLog::QSlowLog() : threshold_(0), logger_(nullptr) { } QSlowLog::~QSlowLog() { } void QSlowLog::SetThreshold(unsigned int v) { threshold_ = v; } void QSlowLog::SetLogLimit(std::size_t maxCount) { logMaxCount_ = maxCount; } void QSlowLog::Begin() { if (!threshold_) return; timeval begin; gettimeofday(&begin, 0); beginUs_ = begin.tv_sec * 1000000 + begin.tv_usec; } void QSlowLog::EndAndStat(const std::vector& cmds) { if (!threshold_ || beginUs_ == 0) return; timeval end; gettimeofday(&end, 0); auto used = end.tv_sec * 1000000 + end.tv_usec - beginUs_; if (used >= threshold_) { if (logger_ == nullptr) logger_ = LogManager::Instance().CreateLog(logALL, logFILE, "slowlog.qedis"); LOG_INF(logger_) << "+ Used:(us) " << used; for (const auto& param : cmds) { LOG_INF(logger_) << param; } if (cmds[0] == "slowlog") return; SlowLogItem item; item.used = static_cast(used); item.cmds = cmds; logs_.emplace_front(std::move(item)); if (logs_.size() > logMaxCount_) logs_.pop_back(); } } } ================================================ FILE: QedisCore/QSlowLog.h ================================================ #ifndef BERT_QSLOWLOG_H #define BERT_QSLOWLOG_H #include #include #include "QString.h" class Logger; namespace qedis { struct SlowLogItem { unsigned used; std::vector cmds; SlowLogItem() : used(0) { } SlowLogItem(SlowLogItem&& item) : used(item.used), cmds(std::move(item.cmds)) { } }; class QSlowLog { public: static QSlowLog& Instance(); QSlowLog(const QSlowLog& ) = delete; void operator= (const QSlowLog& ) = delete; void Begin(); void EndAndStat(const std::vector& cmds); void SetThreshold(unsigned int ); void SetLogLimit(std::size_t maxCount); void ClearLogs() { logs_.clear(); } std::size_t GetLogsCount() const { return logs_.size(); } const std::deque& GetLogs() const { return logs_; } private: QSlowLog(); ~QSlowLog(); unsigned int threshold_; long long beginUs_; Logger* logger_; std::size_t logMaxCount_; std::deque logs_; }; } #endif ================================================ FILE: QedisCore/QSortedSet.cc ================================================ #include "QSortedSet.h" #include "QStore.h" #include "Log/Logger.h" #include namespace qedis { QSortedSet::Member2Score::iterator QSortedSet::FindMember(const QString& member) { return members_.find(member); } void QSortedSet::AddMember(const QString& member, double score) { assert (FindMember(member) == members_.end()); members_.insert(Member2Score::value_type(member, score)); scores_[score].insert(member); } double QSortedSet::UpdateMember(const Member2Score::iterator& itMem, double delta) { auto oldScore = itMem->second; auto newScore = oldScore + delta; itMem->second = newScore; auto itScore(scores_.find(oldScore)); assert (itScore != scores_.end()); size_t ret = itScore->second.erase(itMem->first); assert (ret == 1); bool succ = scores_[newScore].insert(itMem->first).second; assert (succ); return newScore; } int QSortedSet::Rank(const QString& member) const { double score; auto itMem(members_.find(member)); if (itMem != members_.end()) score = itMem->second; else return -1; int rank = 0; for (auto it(scores_.begin()); it != scores_.end(); rank += it->second.size(), ++ it) { if (it->first == score) { auto iter(it->second.begin()); for (; iter != it->second.end(); ++ iter, ++ rank) { if (*iter == member) return rank; } assert (!!!"Why can not find member"); } } assert (!!!"Why can not find score"); return -1; } int QSortedSet::RevRank(const QString& member) const { int rank = Rank(member); if (rank == -1) return rank; return static_cast(members_.size() - (rank + 1)); } bool QSortedSet::DelMember(const QString& member) { double score = 0; Member2Score::const_iterator itMem(members_.find(member)); if (itMem != members_.end()) { score = itMem->second; members_.erase(itMem); } else { return false; } auto it(scores_.find(score)); assert (it != scores_.end()); auto num = it->second.erase(member); assert (num == 1); return true; } QSortedSet::Member2Score::value_type QSortedSet::GetMemberByRank(size_t rank) const { if (rank >= members_.size()) rank = members_.size() - 1; double score = 0; size_t iterRank = 0; for ( auto it(scores_.begin()); it != scores_.end(); iterRank += it->second.size(), ++ it) { if (iterRank + it->second.size() > rank) { assert(iterRank <= rank); score = it->first; auto itMem(it->second.begin()); for (; iterRank != rank; ++ iterRank, ++ itMem) { } DBG << "Get rank " << rank << ", name " << itMem->c_str(); return std::make_pair(*itMem, score); } } return std::make_pair(QString(), score); } size_t QSortedSet::Size() const { return members_.size(); } std::vector QSortedSet::RangeByRank(long start, long end) const { AdjustIndex(start, end, Size()); if (start > end) return std::vector(); std::vector res; for (long rank = start; rank <= end; ++ rank) { res.push_back(GetMemberByRank(rank)); } return res; } std::vector QSortedSet::RangeByScore(double minScore, double maxScore) { if (minScore > maxScore) return std::vector(); auto itMin = scores_.lower_bound(minScore); if (itMin == scores_.end()) return std::vector(); std::vector res; auto itMax = scores_.upper_bound(maxScore); for (; itMin != itMax; ++ itMin) { for (const auto& e : itMin->second) { res.push_back(std::make_pair(e, itMin->first)); } } return res; } QObject QObject::CreateSSet() { QObject obj(QType_sortedSet); obj.Reset(new QSortedSet); return obj; } // commands #define GET_SORTEDSET(name) \ QObject* value; \ QError err = QSTORE.GetValueByType(name, value, QType_sortedSet); \ if (err != QError_ok) { \ ReplyError(err, reply); \ return err; \ } #define GET_OR_SET_SORTEDSET(name) \ QObject* value; \ QError err = QSTORE.GetValueByType(name, value, QType_sortedSet); \ if (err != QError_ok && err != QError_notExist) { \ ReplyError(err, reply); \ return err; \ } \ if (err == QError_notExist) { \ value = QSTORE.SetValue(name, QObject::CreateSSet()); \ } QError zadd(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 != 0) { ReplyError(QError_syntax, reply); return QError_syntax; } GET_OR_SET_SORTEDSET(params[1]); size_t newMembers = 0; auto sset = value->CastSortedSet(); for (size_t i = 2; i < params.size(); i += 2) { double score = 0; if (!Strtod(params[i].c_str(), params[i].size(), &score)) { ReplyError(QError_nan, reply); return QError_nan; } auto it = sset->FindMember(params[i+1]); if (it == sset->end()) { sset->AddMember(params[i+1], score); ++ newMembers; } } FormatInt(newMembers, reply); return QError_ok; } QError zcard(const std::vector& params, UnboundedBuffer* reply) { GET_SORTEDSET(params[1]); auto sset = value->CastSortedSet(); FormatInt(static_cast(sset->Size()), reply); return QError_ok; } QError zrank(const std::vector& params, UnboundedBuffer* reply) { GET_SORTEDSET(params[1]); auto sset = value->CastSortedSet(); int rank = sset->Rank(params[2]); if (rank != -1) FormatInt(rank, reply); else FormatNull(reply); return QError_ok; } QError zrevrank(const std::vector& params, UnboundedBuffer* reply) { GET_SORTEDSET(params[1]); auto sset = value->CastSortedSet(); int rrank = sset->RevRank(params[2]); if (rrank != -1) FormatInt(rrank, reply); else FormatNull(reply); return QError_ok; } QError zrem(const std::vector& params, UnboundedBuffer* reply) { GET_SORTEDSET(params[1]); auto sset = value->CastSortedSet(); long cnt = 0; for (size_t i = 2; i < params.size(); ++ i) { if (sset->DelMember(params[i])) ++ cnt; } FormatInt(cnt, reply); return QError_ok; } QError zincrby(const std::vector& params, UnboundedBuffer* reply) { GET_OR_SET_SORTEDSET(params[1]); double delta; if (!Strtod(params[2].c_str(), params[2].size(), &delta)) { ReplyError(QError_nan, reply); return QError_nan; } double newScore = delta; auto sset = value->CastSortedSet(); auto itMem = sset->FindMember(params[3]); if (itMem == sset->end()) sset->AddMember(params[3], delta); else newScore = sset->UpdateMember(itMem, delta); FormatInt(newScore, reply); return QError_ok; } QError zscore(const std::vector& params, UnboundedBuffer* reply) { GET_SORTEDSET(params[1]); auto sset = value->CastSortedSet(); auto itMem = sset->FindMember(params[2]); if (itMem == sset->end()) FormatNull(reply); else FormatInt(itMem->second, reply); return QError_ok; } static QError GenericRange(const std::vector& params, UnboundedBuffer* reply, bool reverse) { GET_SORTEDSET(params[1]); bool withScore = false; if (params.size() == 5 && strncasecmp(params[4].c_str(), "withscores", 10) == 0) { withScore = true; } else if (params.size() >= 5) { ReplyError(QError_syntax, reply); return QError_syntax; } long start, end; if (!Strtol(params[2].c_str(), params[2].size(), &start) || !Strtol(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_param, reply); return QError_param; } auto sset = value->CastSortedSet(); auto res(sset->RangeByRank(start, end)); if (res.empty()) { FormatNullArray(reply); return QError_ok; } long nBulk = withScore ? res.size() * 2 : res.size(); PreFormatMultiBulk(nBulk, reply); if (!reverse) { for (const auto& s : res) { FormatBulk(s.first, reply); if (withScore) { char score[64]; int len = Double2Str(score, sizeof score, s.second); FormatBulk(score, len, reply); } } } else { for (auto it(res.rbegin()); it != res.rend(); ++ it) { const auto& s = *it; FormatBulk(s.first, reply); if (withScore) { char score[64]; int len = Double2Str(score, sizeof score, s.second); FormatBulk(score, len, reply); } } } return QError_ok; } // zrange key start stop [WITHSCORES] QError zrange(const std::vector& params, UnboundedBuffer* reply) { return GenericRange(params, reply, false); } // zrange key start stop [WITHSCORES] QError zrevrange(const std::vector& params, UnboundedBuffer* reply) { return GenericRange(params, reply, true); } static QError GenericScoreRange(const std::vector& params, UnboundedBuffer* reply, bool reverse) { GET_SORTEDSET(params[1]); bool withScore = false; if (params.size() == 5 && strncasecmp(params[4].c_str(), "withscores", 10) == 0) { withScore = true; } else if (params.size() >= 5) { ReplyError(QError_syntax, reply); return QError_syntax; } long minScore, maxScore; if (!Strtol(params[2].c_str(), params[2].size(), &minScore) || !Strtol(params[3].c_str(), params[3].size(), &maxScore)) { ReplyError(QError_nan, reply); return QError_nan; } auto sset = value->CastSortedSet(); auto res(sset->RangeByScore(minScore, maxScore)); if (res.empty()) { FormatNull(reply); return QError_ok; } long nBulk = withScore ? res.size() * 2 : res.size(); PreFormatMultiBulk(nBulk, reply); if (!reverse) { for (const auto& s : res) { FormatBulk(s.first, reply); if (withScore) { char score[64]; int len = Double2Str(score, sizeof score, s.second); FormatBulk(score, len, reply); } } } else { for (auto it(res.rbegin()); it != res.rend(); ++ it) { const auto& s = *it; FormatBulk(s.first, reply); if (withScore) { char score[64]; int len = Double2Str(score, sizeof score, s.second); FormatBulk(score, len, reply); } } } return QError_ok; } QError zrangebyscore(const std::vector& params, UnboundedBuffer* reply) { return GenericScoreRange(params, reply, false); } QError zrevrangebyscore(const std::vector& params, UnboundedBuffer* reply) { return GenericScoreRange(params, reply, true); } static QError GenericRemRange(const std::vector& params, UnboundedBuffer* reply, bool useRank) { GET_SORTEDSET(params[1]); double start, end; if (!Strtod(params[2].c_str(), params[2].size(), &start) || !Strtod(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_nan, reply); return QError_nan; } std::vector res; auto sset = value->CastSortedSet(); if (useRank) { long lstart = static_cast(start); long lend = static_cast(end); AdjustIndex(lstart, lend, sset->Size()); res = sset->RangeByRank(lstart, lend); } else { res = sset->RangeByScore(start, end); } if (res.empty()) { Format0(reply); return QError_ok; } for (const auto& s : res) { bool succ = sset->DelMember(s.first); assert(succ); } if (sset->Size() == 0) QSTORE.DeleteKey(params[1]); FormatInt(static_cast(res.size()), reply); return QError_ok; } QError zremrangebyrank(const std::vector& params, UnboundedBuffer* reply) { return GenericRemRange(params, reply, true); } QError zremrangebyscore(const std::vector& params, UnboundedBuffer* reply) { return GenericRemRange(params, reply, false); } } ================================================ FILE: QedisCore/QSortedSet.h ================================================ #ifndef BERT_QSORTEDSET_H #define BERT_QSORTEDSET_H #include "QString.h" #include "QHelper.h" #include #include #include #include namespace qedis { class QSortedSet { public: using Members = std::set; using Score2Members = std::map; using Member2Score = std::unordered_map >; Member2Score::iterator FindMember(const QString& member); Member2Score::const_iterator begin() const { return members_.begin(); }; Member2Score::iterator begin() { return members_.begin(); }; Member2Score::const_iterator end() const { return members_.end(); }; Member2Score::iterator end() { return members_.end(); }; void AddMember (const QString& member, double score); double UpdateMember(const Member2Score::iterator& itMem, double delta); int Rank (const QString& member) const;// 0-based int RevRank (const QString& member) const;// 0-based bool DelMember (const QString& member); Member2Score::value_type GetMemberByRank(std::size_t rank) const; std::vector RangeByRank(long start, long end) const; std::vector RangeByScore(double minScore, double maxScore); std::size_t Size() const; private: Score2Members scores_; Member2Score members_; }; } #endif ================================================ FILE: QedisCore/QStore.cc ================================================ #include "QStore.h" #include "QClient.h" #include "QConfig.h" #include "QAOF.h" #include "QMulti.h" #include "Log/Logger.h" #include "QLeveldb.h" #include #include namespace qedis { uint32_t QObject::lruclock = static_cast(::time(nullptr)); QObject::QObject(QType t) : type(t) { switch (type) { case QType_list: encoding = QEncode_list; break; case QType_set: encoding = QEncode_set; break; case QType_sortedSet: encoding = QEncode_sset; break; case QType_hash: encoding = QEncode_hash; break; default: encoding = QEncode_invalid; break; } lru = 0; value = nullptr; } QObject::~QObject() { _FreeValue(); } void QObject::Clear() { _FreeValue(); type = QType_invalid; encoding = QEncode_invalid; lru = 0; value = nullptr; } void QObject::Reset(void* newvalue) { _FreeValue(); value = newvalue; } QObject::QObject(QObject&& obj) : type(QType_invalid), encoding(QEncode_invalid), lru(0), value(nullptr) { _MoveFrom(std::move(obj)); } QObject& QObject::operator= (QObject&& obj) { _MoveFrom(std::move(obj)); return *this; } void QObject::_MoveFrom(QObject&& obj) { this->Reset(); this->encoding = obj.encoding; this->type = obj.type; this->value = obj.value; this->lru = obj.lru; obj.encoding = QEncode_invalid; obj.type = QType_invalid; obj.value = nullptr; obj.lru = 0; } void QObject::_FreeValue() { switch (encoding) { case QEncode_raw: delete CastString(); break; case QEncode_list: delete CastList(); break; case QEncode_set: delete CastSet(); break; case QEncode_sset: delete CastSortedSet(); break; case QEncode_hash: delete CastHash(); break; default: break; } } int QStore::dirty_ = 0; void QStore::ExpiresDB::SetExpire(const QString& key, uint64_t when) { expireKeys_[key] = when; } int64_t QStore::ExpiresDB::TTL(const QString& key, uint64_t now) { if (!QSTORE.ExistsKey(key)) return ExpireResult::notExist; ExpireResult ret = ExpireIfNeed(key, now); switch (ret) { case ExpireResult::expired: case ExpireResult::persist: return ret; default: break; } auto it(expireKeys_.find(key)); return static_cast(it->second - now); } bool QStore::ExpiresDB::ClearExpire(const QString& key) { return ExpireResult::expired == ExpireIfNeed(key, std::numeric_limits::max()); } QStore::ExpireResult QStore::ExpiresDB::ExpireIfNeed(const QString& key, uint64_t now) { auto it(expireKeys_.find(key)); if (it != expireKeys_.end()) { if (it->second > now) return ExpireResult::notExpire; WRN << "Delete timeout key " << it->first; QSTORE.DeleteKey(it->first); expireKeys_.erase(it); return ExpireResult::expired; } return ExpireResult::persist; } int QStore::ExpiresDB::LoopCheck(uint64_t now) { const int kMaxDel = 100; const int kMaxCheck = 2000; int nDel = 0; int nLoop = 0; for (auto it = expireKeys_.begin(); it!= expireKeys_.end() && nDel < kMaxDel && nLoop < kMaxCheck; ++ nLoop) { if (it->second <= now) { // time to delete INF << "LoopCheck try delete key:" << it->first; std::vector params{"del", it->first}; Propogate(params); QSTORE.DeleteKey(it->first); expireKeys_.erase(it ++); ++ nDel; } else { ++ it; } } return nDel; } bool QStore::BlockedClients::BlockClient(const QString& key, QClient* client, uint64_t timeout, ListPosition pos, const QString* target) { if (!client->WaitFor(key, target)) { ERR << key << " is already waited by " << client->GetName(); return false; } Clients& clients = blockedClients_[key]; clients.push_back(Clients::value_type(std::static_pointer_cast(client->shared_from_this()), timeout, pos)); INF << key << " is waited by " << client->GetName() << ", timeout " << timeout; return true; } size_t QStore::BlockedClients::UnblockClient(QClient* client) { size_t n = 0; const auto& keys = client->WaitingKeys(); for (const auto& key : keys) { Clients& clients = blockedClients_[key]; assert(!clients.empty()); for (auto it(clients.begin()); it != clients.end(); ++ it) { auto cli(std::get<0>(*it).lock()); if (cli && cli.get() == client) { INF << "unblock " << client->GetName() << " for key " << key; clients.erase(it); ++ n; break; } } } client->ClearWaitingKeys(); return n; } size_t QStore::BlockedClients::ServeClient(const QString& key, const PLIST& list) { assert(!list->empty()); auto it = blockedClients_.find(key); if (it == blockedClients_.end()) return 0; Clients& clients = it->second; if (clients.empty()) return 0; size_t nServed = 0; while (!list->empty() && !clients.empty()) { auto cli(std::get<0>(clients.front()).lock()); auto pos(std::get<2>(clients.front())); if (cli) { bool errorTarget = false; const QString& target = cli->GetTarget(); QObject* dst = nullptr; if (!target.empty()) { INF << list->front() << " is try lpush to target list " << target; // check target list QError err = QSTORE.GetValueByType(target, dst, QType_list); if (err != QError_ok) { if (err != QError_notExist) { UnboundedBuffer reply; ReplyError(err, &reply); cli->SendPacket(reply); errorTarget = true; } else { dst = QSTORE.SetValue(target, QObject::CreateList()); } } } if (!errorTarget) { if (dst) { auto dstlist = dst->CastList(); dstlist->push_front(list->back()); INF << list->front() << " success lpush to target list " << target; std::vector params{"lpush", target, list->back()}; Propogate(params); } UnboundedBuffer reply; if (!dst) { PreFormatMultiBulk(2, &reply); FormatBulk(key, &reply); } if (pos == ListPosition::head) { FormatBulk(list->front(), &reply); list->pop_front(); std::vector params{"lpop", key}; Propogate(params); } else { FormatBulk(list->back(), &reply); list->pop_back(); std::vector params{"rpop", key}; Propogate(params); } cli->SendPacket(reply); INF << "Serve client " << cli->GetName() << " list key : " << key; } UnblockClient(cli.get()); ++ nServed; } else { clients.pop_front(); } } return nServed; } int QStore::BlockedClients::LoopCheck(uint64_t now) { int n = 0; for (auto it(blockedClients_.begin()); it != blockedClients_.end() && n < 100; ) { Clients& clients = it->second; for (auto cli(clients.begin()); cli != clients.end(); ) { if (std::get<1>(*cli) < now) // timeout { ++ n; const QString& key = it->first; auto scli(std::get<0>(*cli).lock()); if (scli && scli->WaitingKeys().count(key)) { INF << scli->GetName() << " is timeout for waiting key " << key; UnboundedBuffer reply; FormatNull(&reply); scli->SendPacket(reply); scli->ClearWaitingKeys(); } clients.erase(cli ++); } else { ++ cli; } } if (clients.empty()) { blockedClients_.erase(it ++); } else { ++ it; } } return n; } QStore& QStore::Instance() { static QStore store; return store; } void QStore::Init(int dbNum) { if (dbNum < 1) dbNum = 1; else if (dbNum > kMaxDbNum) dbNum = kMaxDbNum; store_.resize(dbNum); expiresDb_.resize(dbNum); blockedClients_.resize(dbNum); } int QStore::LoopCheckExpire(uint64_t now) { return expiresDb_[dbno_].LoopCheck(now); } int QStore::LoopCheckBlocked(uint64_t now) { return blockedClients_[dbno_].LoopCheck(now); } int QStore::SelectDB(int dbno) { if (dbno == dbno_) return dbno_; if (dbno >= 0 && dbno < static_cast(store_.size())) { int oldDb = dbno_; dbno_ = dbno; return oldDb; } return -1; } int QStore::GetDB() const { return dbno_; } const QObject* QStore::GetObject(const QString& key) const { auto db = &store_[dbno_]; QDB::const_iterator it(db->find(key)); if (it != db->end()) return &it->second; if (!backends_.empty()) { // if it's in dirty list, it must be deleted, wait sync to backend if (waitSyncKeys_[dbno_].count(key)) return nullptr; // load from leveldb, if has, insert to qedis cache QObject obj = backends_[dbno_]->Get(key); if (obj.type != QType_invalid) { DBG << "GetKey from leveldb:" << key; QObject& realobj = ((*db)[key] = std::move(obj)); realobj.lru = QObject::lruclock; // trick: use lru field to store the remain seconds to be expired. unsigned int remainTtlSeconds = obj.lru; if (remainTtlSeconds > 0) SetExpire(key, ::Now() + remainTtlSeconds * 1000); return &realobj; } } return nullptr; } bool QStore::DeleteKey(const QString& key) { auto db = &store_[dbno_]; // add to dirty queue if (!waitSyncKeys_.empty()) { waitSyncKeys_[dbno_][key] = nullptr; // null implies delete data } return db->erase(key) != 0; } bool QStore::ExistsKey(const QString& key) const { const QObject* obj = GetObject(key); return obj != nullptr; } QType QStore::KeyType(const QString& key) const { const QObject* obj = GetObject(key); if (!obj) return QType_invalid; return QType(obj->type); } static bool RandomMember(const QDB& hash, QString& res, QObject** val) { QDB::const_local_iterator it = RandomHashMember(hash); if (it != QDB::const_local_iterator()) { res = it->first; if (val) *val = const_cast(&it->second); return true; } return false; } QString QStore::RandomKey(QObject** val) const { QString res; if (!store_.empty() && !store_[dbno_].empty()) RandomMember(store_[dbno_], res, val); return res; } size_t QStore::ScanKey(size_t cursor, size_t count, std::vector& res) const { if (store_.empty() || store_[dbno_].empty()) return 0; std::vector iters; size_t newCursor = ScanHashMember(store_[dbno_], cursor, count, iters); res.reserve(iters.size()); for (auto it : iters) res.push_back(it->first); return newCursor; } QError QStore::GetValue(const QString& key, QObject*& value, bool touch) { if (touch) return GetValueByType(key, value); else return GetValueByTypeNoTouch(key, value); } QError QStore::GetValueByType(const QString& key, QObject*& value, QType type) { return _GetValueByType(key, value, type, true); } QError QStore::GetValueByTypeNoTouch(const QString& key, QObject*& value, QType type) { return _GetValueByType(key, value, type, false); } QError QStore::_GetValueByType(const QString& key, QObject*& value, QType type, bool touch) { if (_ExpireIfNeed(key, ::Now()) == ExpireResult::expired) return QError_notExist; auto cobj = GetObject(key); if (cobj) { if (type != QType_invalid && type != QType(cobj->type)) { return QError_type; } else { value = const_cast(cobj); // Do not update if child process exists extern pid_t g_qdbPid; if (touch && g_rewritePid == -1 && g_qdbPid == -1) value->lru = QObject::lruclock; return QError_ok; } } else { return QError_notExist; } return QError_ok; // never here } QObject* QStore::SetValue(const QString& key, QObject&& value) { auto db = &store_[dbno_]; QObject& obj = ((*db)[key] = std::move(value)); obj.lru = QObject::lruclock; // put this key to sync list if (!waitSyncKeys_.empty()) waitSyncKeys_[dbno_][key] = &obj; return &obj; } void QStore::SetExpire(const QString& key, uint64_t when) const { expiresDb_[dbno_].SetExpire(key, when); } void QStore::SetExpireAfter(const QString& key, uint64_t ttl) const { SetExpire(key, ::Now() + ttl); } int64_t QStore::TTL(const QString& key, uint64_t now) { return expiresDb_[dbno_].TTL(key, now); } bool QStore::ClearExpire(const QString& key) { return expiresDb_[dbno_].ClearExpire(key); } QStore::ExpireResult QStore::_ExpireIfNeed(const QString& key, uint64_t now) { return expiresDb_[dbno_].ExpireIfNeed(key, now); } void QStore::InitExpireTimer() { for (int i = 0; i < static_cast(expiresDb_.size()); ++ i) { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(1); timer->SetCallback([&, i] () { int oldDb = QSTORE.SelectDB(i); QSTORE.LoopCheckExpire(::Now()); QSTORE.SelectDB(oldDb); }); TimerManager::Instance().AddTimer(timer); } } void QStore::ResetDb() { std::vector(store_.size()).swap(store_); std::vector(expiresDb_.size()).swap(expiresDb_); std::vector(blockedClients_.size()).swap(blockedClients_); dbno_ = 0; } size_t QStore::BlockedSize() const { size_t s = 0; for (const auto& b : blockedClients_) s += b.Size(); return s; } bool QStore::BlockClient(const QString& key, QClient* client, uint64_t timeout, ListPosition pos, const QString* dstList) { return blockedClients_[dbno_].BlockClient(key, client, timeout, pos, dstList); } size_t QStore::UnblockClient(QClient* client) { return blockedClients_[dbno_].UnblockClient(client); } size_t QStore::ServeClient(const QString& key, const PLIST& list) { return blockedClients_[dbno_].ServeClient(key, list); } void QStore::InitBlockedTimer() { for (int i = 0; i < static_cast(blockedClients_.size()); ++ i) { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(3); timer->SetCallback([&, i] () { int oldDb = QSTORE.SelectDB(i); QSTORE.LoopCheckBlocked(::Now()); QSTORE.SelectDB(oldDb); }); TimerManager::Instance().AddTimer(timer); } } static void EvictItems() { QObject::lruclock = static_cast(::time(nullptr)); QObject::lruclock &= kMaxLRUValue; int currentDb = QSTORE.GetDB(); QEDIS_DEFER { QSTORE.SelectDB(currentDb); }; int tryCnt = 0; size_t usedMem = 0; while (tryCnt ++ < 32 && (usedMem = getMemoryInfo(VmRSS)) > g_config.maxmemory) { if (g_config.noeviction) { WRN << "noeviction policy, but memory usage exceeds: " << usedMem; return; } for (int dbno = 0; true; ++ dbno) { if (QSTORE.SelectDB(dbno) == -1) break; if (QSTORE.DBSize() == 0) continue; QString evictKey; uint32_t choosedIdle = 0; for (int i = 0; i < g_config.maxmemorySamples; ++ i) { QObject* val = nullptr; auto key = QSTORE.RandomKey(&val); if (!val) continue; auto idle = EstimateIdleTime(val->lru); if (evictKey.empty() || choosedIdle < idle) { evictKey = std::move(key); choosedIdle = idle; } } if (!evictKey.empty()) { QSTORE.DeleteKey(evictKey); WRN << "Evict '" << evictKey << "' in db " << dbno << ", idle time: " << choosedIdle << ", used mem: " << usedMem; } } } } uint32_t EstimateIdleTime(uint32_t lru) { if (lru <= QObject::lruclock) return QObject::lruclock - lru; else return (kMaxLRUValue - lru) + QObject::lruclock; } void QStore::InitEvictionTimer() { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(1000); // emit eviction every second. timer->SetCallback([] () { EvictItems(); }); TimerManager::Instance().AddTimer(timer); } void QStore::InitDumpBackends() { assert (waitSyncKeys_.empty()); if (g_config.backend == BackEndNone) return; if (g_config.backend == BackEndLeveldb) { waitSyncKeys_.resize(store_.size()); for (size_t i = 0; i < store_.size(); ++ i) { std::unique_ptr db(new QLeveldb); QString dbpath = g_config.backendPath + std::to_string(i); if (!db->Open(dbpath.data())) assert(false); else USR << "Open leveldb " << dbpath; backends_.push_back(std::move(db)); } } else { // ERROR: unsupport backend return; } for (int i = 0; i < static_cast(backends_.size()); ++ i) { auto timer = TimerManager::Instance().CreateTimer(); timer->Init(1000 / g_config.backendHz); timer->SetCallback([&, i] () { int oldDb = QSTORE.SelectDB(i); QSTORE.DumpToBackends(i); QSTORE.SelectDB(oldDb); }); TimerManager::Instance().AddTimer(timer); } } void QStore::DumpToBackends(int dbno) { if (static_cast(waitSyncKeys_.size()) <= dbno) return; const int kMaxSync = 100; int processed = 0; auto& dirtyKeys = waitSyncKeys_[dbno]; uint64_t now = ::Now(); for (auto it = dirtyKeys.begin(); processed++ < kMaxSync && it != dirtyKeys.end(); ) { // check ttl int64_t when = QSTORE.TTL(it->first, now); if (it->second && when != QStore::ExpireResult::expired) { assert (when != QStore::ExpireResult::notExpire); if (when > 0) when += now; backends_[dbno]->Put(it->first, *it->second, when); DBG << "UPDATE leveldb key " << it->first << ", when = " << when; } else { backends_[dbno]->Delete(it->first); DBG << "DELETE leveldb key " << it->first; } it = dirtyKeys.erase(it); } } void QStore::AddDirtyKey(const QString& key) { // put this key to sync list if (!waitSyncKeys_.empty()) { QObject* obj = nullptr; GetValue(key, obj); waitSyncKeys_[dbno_][key] = obj; } } void QStore::AddDirtyKey(const QString& key, const QObject* value) { // put this key to sync list if (!waitSyncKeys_.empty()) waitSyncKeys_[dbno_][key] = value; } std::vector g_dirtyKeys; void Propogate(const std::vector& params) { assert (!params.empty()); if (!g_dirtyKeys.empty()) { for (const auto& k : g_dirtyKeys) { ++ QStore::dirty_; QMulti::Instance().NotifyDirty(QSTORE.GetDB(), k); QSTORE.AddDirtyKey(k); // TODO optimize } g_dirtyKeys.clear(); } else if (params.size() > 1) { ++ QStore::dirty_; QMulti::Instance().NotifyDirty(QSTORE.GetDB(), params[1]); QSTORE.AddDirtyKey(params[1]); // TODO optimize } if (g_config.appendonly) QAOFThreadController::Instance().SaveCommand(params, QSTORE.GetDB()); QREPL.SendToSlaves(params); } void Propogate(int dbno, const std::vector& params) { QMulti::Instance().NotifyDirtyAll(dbno); Propogate(params); } } ================================================ FILE: QedisCore/QStore.h ================================================ #ifndef BERT_QSTORE_H #define BERT_QSTORE_H #include "QCommon.h" #include "QSet.h" #include "QSortedSet.h" #include "QHash.h" #include "QList.h" #include "Timer.h" #include "QDumpInterface.h" #include #include #include namespace qedis { using PSTRING = QString*; using PLIST = QList*; using PSET = QSet*; using PSSET = QSortedSet*; using PHASH = QHash*; static const int kLRUBits = 24; static const uint32_t kMaxLRUValue = (1 << kLRUBits) - 1; uint32_t EstimateIdleTime(uint32_t lru); struct QObject { public: static uint32_t lruclock; unsigned int type : 4; unsigned int encoding : 4; unsigned int lru : kLRUBits; void* value; explicit QObject(QType = QType_invalid); ~QObject(); QObject(const QObject& obj) = delete; QObject& operator= (const QObject& obj) = delete; QObject(QObject&& obj); QObject& operator= (QObject&& obj); void Clear(); void Reset(void* newvalue = nullptr); static QObject CreateString(const QString& value); static QObject CreateString(long value); static QObject CreateList(); static QObject CreateSet(); static QObject CreateSSet(); static QObject CreateHash(); PSTRING CastString() const { return reinterpret_cast(value); } PLIST CastList() const { return reinterpret_cast(value); } PSET CastSet() const { return reinterpret_cast(value); } PSSET CastSortedSet() const { return reinterpret_cast(value); } PHASH CastHash() const { return reinterpret_cast(value); } private: void _MoveFrom(QObject&& obj); void _FreeValue(); }; class QClient; using QDB = std::unordered_map >; const int kMaxDbNum = 65536; class QStore { public: static QStore& Instance(); QStore(const QStore& ) = delete; void operator= (const QStore& ) = delete; void Init(int dbNum = 16); int SelectDB(int dbno); int GetDB() const; // Key operation bool DeleteKey(const QString& key); bool ExistsKey(const QString& key) const; QType KeyType(const QString& key) const; QString RandomKey(QObject** val = nullptr) const; size_t DBSize() const { return store_[dbno_].size(); } size_t ScanKey(size_t cursor, size_t count, std::vector& res) const; // iterator QDB::const_iterator begin() const { return store_[dbno_].begin(); } QDB::const_iterator end() const { return store_[dbno_].end(); } QDB::iterator begin() { return store_[dbno_].begin(); } QDB::iterator end() { return store_[dbno_].end(); } const QObject* GetObject(const QString& key) const; QError GetValue(const QString& key, QObject*& value, bool touch = true); QError GetValueByType(const QString& key, QObject*& value, QType type = QType_invalid); // do not update lru time QError GetValueByTypeNoTouch(const QString& key, QObject*& value, QType type = QType_invalid); QObject* SetValue(const QString& key, QObject&& value); // for expire key enum ExpireResult : std::int8_t { notExpire= 0, persist = -1, expired = -2, notExist = -2, }; void SetExpire(const QString& key, uint64_t when) const; void SetExpireAfter(const QString& key, uint64_t ttl) const; int64_t TTL(const QString& key, uint64_t now); bool ClearExpire(const QString& key); int LoopCheckExpire(uint64_t now); void InitExpireTimer(); // danger cmd void ClearCurrentDB() { store_[dbno_].clear(); } void ResetDb(); // for blocked list bool BlockClient(const QString& key, QClient* client, uint64_t timeout, ListPosition pos, const QString* dstList = 0); size_t UnblockClient(QClient* client); size_t ServeClient(const QString& key, const PLIST& list); int LoopCheckBlocked(uint64_t now); void InitBlockedTimer(); size_t BlockedSize() const; static int dirty_; // eviction timer for lru void InitEvictionTimer(); // for backends void InitDumpBackends(); void DumpToBackends(int dbno); void AddDirtyKey(const QString& key); void AddDirtyKey(const QString& key, const QObject* value); private: QStore() : dbno_(0) { } QError _GetValueByType(const QString& key, QObject*& value, QType type = QType_invalid, bool touch = true); ExpireResult _ExpireIfNeed(const QString& key, uint64_t now); class ExpiresDB { public: void SetExpire(const QString& key, uint64_t when); int64_t TTL(const QString& key, uint64_t now); bool ClearExpire(const QString& key); ExpireResult ExpireIfNeed(const QString& key, uint64_t now); int LoopCheck(uint64_t now); private: using Q_EXPIRE_DB = std::unordered_map >; Q_EXPIRE_DB expireKeys_; // all the keys to be expired, unorder. }; class BlockedClients { public: bool BlockClient(const QString& key, QClient* client, uint64_t timeout, ListPosition pos, const QString* dstList = 0); size_t UnblockClient(QClient* client); size_t ServeClient(const QString& key, const PLIST& list); int LoopCheck(uint64_t now); size_t Size() const { return blockedClients_.size(); } private: using Clients = std::list, uint64_t, ListPosition> >; using WaitingList = std::unordered_map; WaitingList blockedClients_; }; QError _SetValue(const QString& key, QObject& value, bool exclusive = false); // Because GetObject() must be const, so mutable them mutable std::vector store_; mutable std::vector expiresDb_; std::vector blockedClients_; std::vector > backends_; using ToSyncDb = std::unordered_map >; std::vector waitSyncKeys_; int dbno_; }; #define QSTORE QStore::Instance() // ugly, but I don't want to write signalModifiedKey() every where extern std::vector g_dirtyKeys; extern void Propogate(const std::vector& params); extern void Propogate(int dbno, const std::vector& params); } #endif ================================================ FILE: QedisCore/QString.cc ================================================ #include "QString.h" #include "QStore.h" #include "Log/Logger.h" #include namespace qedis { QObject QObject::CreateString(const QString& value) { QObject obj(QType_string); long val; if (Strtol(value.c_str(), value.size(), &val)) { obj.encoding = QEncode_int; obj.value = (void*)val; DBG << "set long value " << val; } else { obj.encoding = QEncode_raw; obj.value = new QString(value); } return obj; } QObject QObject::CreateString(long val) { QObject obj(QType_string); obj.encoding = QEncode_int; obj.value = (void*)val; return obj; } static void DeleteString(QString* s) { delete s; } static void NotDeleteString(QString* ) { } std::unique_ptr GetDecodedString(const QObject* value) { if (value->encoding == QEncode_raw) { return std::unique_ptr(value->CastString(), NotDeleteString); } else if (value->encoding == QEncode_int) { intptr_t val = (intptr_t)value->value; char vbuf[32]; snprintf(vbuf, sizeof vbuf - 1, "%ld", val); return std::unique_ptr(new QString(vbuf), DeleteString); } else { assert (!!!"error string encoding"); } return std::unique_ptr(nullptr, NotDeleteString); } static bool SetValue(const QString& key, const QString& value, bool exclusive = false) { if (exclusive) { QObject* val; if (QSTORE.GetValue(key, val) == QError_ok) return false; } QSTORE.ClearExpire(key); // clear key's old ttl QSTORE.SetValue(key, QObject::CreateString(value)); return true; } QError set(const std::vector& params, UnboundedBuffer* reply) { SetValue(params[1], params[2]); FormatOK(reply); return QError_ok; } QError setnx(const std::vector& params, UnboundedBuffer* reply) { if (SetValue(params[1], params[2], true)) Format1(reply); else Format0(reply); return QError_ok; } QError mset(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 != 1) { ReplyError(QError_param, reply); return QError_param; } for (size_t i = 1; i < params.size(); i += 2) { g_dirtyKeys.push_back(params[i]); SetValue(params[i], params[i + 1]); } FormatOK(reply); return QError_ok; } QError msetnx(const std::vector& params, UnboundedBuffer* reply) { if (params.size() % 2 != 1) { ReplyError(QError_param, reply); return QError_param; } for (size_t i = 1; i < params.size(); i += 2) { QObject* val; if (QSTORE.GetValue(params[i], val) == QError_ok) { Format0(reply); return QError_ok; } } for (size_t i = 1; i < params.size(); i += 2) { g_dirtyKeys.push_back(params[i]); SetValue(params[i], params[i + 1]); } Format1(reply); return QError_ok; } QError setex(const std::vector& params, UnboundedBuffer* reply) { long seconds; if (!Strtol(params[2].c_str(), params[2].size(), &seconds)) { ReplyError(QError_nan, reply); return QError_nan; } const auto& key = params[1]; QSTORE.SetValue(key, QObject::CreateString(params[3])); QSTORE.SetExpire(key, ::Now() + seconds * 1000); FormatOK(reply); return QError_ok; } QError psetex(const std::vector& params, UnboundedBuffer* reply) { long milliseconds; if (!Strtol(params[2].c_str(), params[2].size(), &milliseconds)) { ReplyError(QError_nan, reply); return QError_nan; } const auto& key = params[1]; QSTORE.SetValue(key, QObject::CreateString(params[3])); QSTORE.SetExpire(key, ::Now() + milliseconds); FormatOK(reply); return QError_ok; } QError setrange(const std::vector& params, UnboundedBuffer* reply) { long offset; if (!Strtol(params[2].c_str(), params[2].size(), &offset)) { ReplyError(QError_nan, reply); return QError_nan; } QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err != QError_ok) { if (err == QError_notExist) { value = QSTORE.SetValue(params[1], QObject::CreateString("")); } else { ReplyError(err, reply); return err; } } auto str = GetDecodedString(value); const size_t newSize = offset + params[3].size(); if (newSize > str->size()) str->resize(newSize, '\0'); str->replace(offset, params[3].size(), params[3]); if (value->encoding == QEncode_int) { value->Reset(new QString(*str)); value->encoding = QEncode_raw; } FormatInt(static_cast(str->size()), reply); return QError_ok; } static void AddReply(QObject* value, UnboundedBuffer* reply) { auto str = GetDecodedString(value); FormatBulk(str->c_str(), str->size(), reply); } QError get(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err != QError_ok) { if (err == QError_notExist) FormatNull(reply); else ReplyError(err, reply); return err; } AddReply(value, reply); return QError_ok; } QError mget(const std::vector& params, UnboundedBuffer* reply) { PreFormatMultiBulk(params.size() - 1, reply); for (size_t i = 1; i < params.size(); ++ i) { QObject* value; QError err = QSTORE.GetValueByType(params[i], value, QType_string); if (err != QError_ok) FormatNull(reply); else AddReply(value, reply); } return QError_ok; } QError getrange(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err != QError_ok) { if (err == QError_notExist) FormatBulk("", 0, reply); else ReplyError(err, reply); return err; } long start = 0, end = 0; if (!Strtol(params[2].c_str(), params[2].size(), &start) || !Strtol(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_nan, reply); return QError_nan; } auto str = GetDecodedString(value); AdjustIndex(start, end, str->size()); if (start <= end) FormatBulk(&(*str)[start], end - start + 1, reply); else FormatEmptyBulk(reply); return QError_ok; } QError getset(const std::vector& params, UnboundedBuffer* reply) { QObject* value = nullptr; QError err = QSTORE.GetValueByType(params[1], value, QType_string); switch (err) { case QError_notExist: // fall through case QError_ok: if (!value) FormatNull(reply); else FormatBulk(*GetDecodedString(value), reply); QSTORE.SetValue(params[1], QObject::CreateString(params[2])); break; default: ReplyError(err, reply); return err; } return QError_ok; } QError append(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); switch (err) { case QError_ok: { auto s = GetDecodedString(value); value = QSTORE.SetValue(params[1], QObject::CreateString(*s + params[2])); } break; case QError_notExist: value = QSTORE.SetValue(params[1], QObject::CreateString(params[2])); break; default: ReplyError(err, reply); return err; }; auto s = GetDecodedString(value); FormatInt(static_cast(s->size()), reply); return QError_ok; } QError bitcount(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err != QError_ok) { if (err == QError_type) ReplyError(QError_type, reply); else Format0(reply); return QError_ok; } if (params.size() != 2 && params.size() != 4) { ReplyError(QError_param, reply); return QError_param; } long start = 0; long end = -1; if (params.size() == 4) { if (!Strtol(params[2].c_str(), params[2].size(), &start) || !Strtol(params[3].c_str(), params[3].size(), &end)) { ReplyError(QError_nan, reply); return QError_nan; } } auto str = GetDecodedString(value); AdjustIndex(start, end, str->size()); size_t cnt = 0; if (end >= start) { cnt = BitCount((const uint8_t*)str->data() + start, end - start + 1); } FormatInt(static_cast(cnt), reply); return QError_ok; } QError getbit(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err != QError_ok) { Format0(reply); return QError_ok; } long offset = 0; if (!Strtol(params[2].c_str(), params[2].size(), &offset)) { ReplyError(QError_nan, reply); return QError_nan; } auto str = GetDecodedString(value); const uint8_t* buf = (const uint8_t*)str->c_str(); size_t size = 8 * str->size(); if (offset < 0 || offset >= static_cast(size)) { Format0(reply); return QError_ok; } size_t bytesOffset = offset / 8; size_t bitsOffset = offset % 8; uint8_t byte = buf[bytesOffset]; if (byte & (0x1 << bitsOffset)) Format1(reply); else Format0(reply); return QError_ok; } QError setbit(const std::vector& params, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(params[1], value, QType_string); if (err == QError_notExist) { value = QSTORE.SetValue(params[1], QObject::CreateString("")); err = QError_ok; } if (err != QError_ok) { Format0(reply); return err; } long offset = 0; long on = 0; if (!Strtol(params[2].c_str(), params[2].size(), &offset) || !Strtol(params[3].c_str(), params[3].size(), &on)) { ReplyError(QError_nan, reply); return QError_nan; } if (offset < 0 || offset > kStringMaxBytes) { Format0(reply); return QError_ok; } QString newVal(*GetDecodedString(value)); size_t bytes = offset / 8; size_t bits = offset % 8; if (bytes + 1 > newVal.size()) newVal.resize(bytes + 1, '\0'); const char oldByte = newVal[bytes]; char& byte = newVal[bytes]; if (on) byte |= (0x1 << bits); else byte &= ~(0x1 << bits); value->Reset(new QString(newVal)); value->encoding = QEncode_raw; FormatInt((oldByte & (0x1 << bits)) ? 1 : 0, reply); return QError_ok; } static QError ChangeFloatValue(const QString& key, float delta, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(key, value, QType_string); if (err == QError_notExist) { value = QSTORE.SetValue(key, QObject::CreateString("0")); err = QError_ok; } if (err != QError_ok) { ReplyError(err, reply); return err; } auto val = GetDecodedString(value); float oldVal = 0; if (!Strtof(val->c_str(), val->size(), &oldVal)) { ReplyError(QError_nan, reply); return QError_nan; } char newVal[32]; int len = snprintf(newVal, sizeof newVal - 1, "%.6g", (oldVal + delta)); value->Reset(new QString(newVal, len)); value->encoding = QEncode_raw; FormatBulk(newVal, len, reply); return QError_ok; } QError incrbyfloat(const std::vector& params, UnboundedBuffer* reply) { float delta = 0; if (!Strtof(params[2].c_str(), params[2].size(), &delta)) { ReplyError(QError_nan, reply); return QError_nan; } return ChangeFloatValue(params[1], delta, reply); } static QError ChangeIntValue(const QString& key, long delta, UnboundedBuffer* reply) { QObject* value; QError err = QSTORE.GetValueByType(key, value, QType_string); if (err == QError_notExist) { value = QSTORE.SetValue(key, QObject::CreateString(0)); err = QError_ok; } if (err != QError_ok) { ReplyError(err, reply); return err; } if (value->encoding != QEncode_int) { ReplyError(QError_nan, reply); return QError_ok; } intptr_t oldVal = (intptr_t)value->value; value->Reset((void*)(oldVal + delta)); FormatInt(oldVal + delta, reply); return QError_ok; } QError incr(const std::vector& params, UnboundedBuffer* reply) { return ChangeIntValue(params[1], 1, reply); } QError decr(const std::vector& params, UnboundedBuffer* reply) { return ChangeIntValue(params[1], -1, reply); } QError incrby(const std::vector& params, UnboundedBuffer* reply) { long delta = 0; if (!Strtol(params[2].c_str(), params[2].size(), &delta)) { ReplyError(QError_nan, reply); return QError_nan; } return ChangeIntValue(params[1], delta, reply); } QError decrby(const std::vector& params, UnboundedBuffer* reply) { long delta = 0; if (!Strtol(params[2].c_str(), params[2].size(), &delta)) { ReplyError(QError_nan, reply); return QError_nan; } return ChangeIntValue(params[1], -delta, reply); } QError strlen(const std::vector& params, UnboundedBuffer* reply) { QObject* val; QError err = QSTORE.GetValueByType(params[1], val, QType_string); if (err != QError_ok) { Format0(reply); return err; } auto str = GetDecodedString(val); FormatInt(static_cast(str->size()), reply); return QError_ok; } enum BitOp { BitOp_and, BitOp_or, BitOp_not, BitOp_xor, }; static QString StringBitOp(const std::vector& keys, BitOp op) { QString res; switch (op) { case BitOp_and: case BitOp_or: case BitOp_xor: for (auto k : keys) { QObject* val; if (QSTORE.GetValueByType(*k, val, QType_string) != QError_ok) continue; auto str = GetDecodedString(val); if (res.empty()) { res = *str; continue; } if (str->size() > res.size()) res.resize(str->size()); for (size_t i = 0; i < str->size(); ++ i) { if (op == BitOp_and) res[i] &= (*str)[i]; else if (op == BitOp_or) res[i] |= (*str)[i]; else if (op == BitOp_xor) res[i] ^= (*str)[i]; } } break; case BitOp_not: { assert(keys.size() == 1); QObject* val; if (QSTORE.GetValueByType(*keys[0], val, QType_string) != QError_ok) break; auto str = GetDecodedString(val); res.resize(str->size()); for (size_t i = 0; i < str->size(); ++ i) { res[i] = ~(*str)[i]; } break; } default: break; } return res; } QError bitop(const std::vector& params, UnboundedBuffer* reply) { std::vector keys; for (size_t i = 3; i < params.size(); ++ i) keys.push_back(¶ms[i]); QError err = QError_param; QString res; if (params[1].size() == 2) { if (strncasecmp(params[1].c_str(), "or", 2) == 0) { err = QError_ok; res = StringBitOp(keys, BitOp_or); } } else if (params[1].size() == 3) { if (strncasecmp(params[1].c_str(), "xor", 3) == 0) { err = QError_ok; res = StringBitOp(keys, BitOp_xor); } else if (strncasecmp(params[1].c_str(), "and", 3) == 0) { err = QError_ok; res = StringBitOp(keys, BitOp_and); } else if (strncasecmp(params[1].c_str(), "not", 3) == 0) { if (params.size() == 4) { err = QError_ok; res = StringBitOp(keys, BitOp_not); } } else { ; } } if (err != QError_ok) { ReplyError(err, reply); } else { QSTORE.SetValue(params[2], QObject::CreateString(res)); FormatInt(static_cast(res.size()), reply); } return QError_ok; } } ================================================ FILE: QedisCore/QString.h ================================================ #ifndef BERT_QSTRING_H #define BERT_QSTRING_H #include #include namespace qedis { using QString = std::string; //typedef std::basic_string, Bert::Allocator > QString; struct QObject; std::unique_ptr GetDecodedString(const QObject* value); } #endif ================================================ FILE: QedisCore/crc64.c ================================================ /* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0. * * Specification of this CRC64 variant follows: * Name: crc-64-jones * Width: 64 bites * Poly: 0xad93d23594c935a9 * Reflected In: True * Xor_In: 0xffffffffffffffff * Reflected_Out: True * Xor_Out: 0x0 * Check("123456789"): 0xe9c6d914c4b8d9ca * * Copyright (c) 2012, Salvatore Sanfilippo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include static const uint64_t crc64_tab[256] = { UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979), UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b), UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6), UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04), UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c), UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe), UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183), UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371), UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8), UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a), UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077), UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285), UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d), UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f), UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02), UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0), UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b), UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489), UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4), UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206), UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e), UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc), UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81), UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73), UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa), UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08), UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75), UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87), UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f), UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d), UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100), UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2), UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416), UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4), UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299), UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b), UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63), UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891), UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec), UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e), UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97), UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965), UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18), UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea), UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2), UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710), UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d), UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f), UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14), UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6), UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b), UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69), UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561), UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793), UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee), UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c), UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495), UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667), UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a), UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8), UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0), UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812), UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f), UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d), UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc), UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e), UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643), UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1), UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9), UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b), UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836), UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4), UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d), UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf), UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2), UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30), UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138), UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca), UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7), UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545), UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce), UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c), UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941), UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3), UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb), UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349), UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734), UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6), UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f), UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd), UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0), UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432), UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a), UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8), UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5), UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47), UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3), UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51), UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c), UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de), UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6), UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124), UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559), UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab), UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222), UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0), UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad), UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f), UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57), UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5), UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8), UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a), UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1), UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053), UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e), UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc), UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4), UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26), UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b), UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9), UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20), UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2), UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf), UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d), UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355), UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7), UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da), UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728), }; uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) { uint64_t j; for (j = 0; j < l; j++) { uint8_t byte = s[j]; crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8); } return crc; } /* Test main */ #ifdef TEST_MAIN #include int main(void) { printf("e9c6d914c4b8d9ca == %016llx\n", (unsigned long long) crc64(0,(unsigned char*)"123456789",9)); return 0; } #endif ================================================ FILE: QedisCore/redisIntset.c ================================================ /* * Copyright (c) 2009-2012, Pieter Noordhuis * Copyright (c) 2009-2012, Salvatore Sanfilippo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "redisIntset.h" #define memrev16ifbe(x) (x) #define memrev32ifbe(x) (x) #define memrev64ifbe(x) (x) #define intrev16ifbe(x) (x) #define intrev32ifbe(x) (x) /* Note that these encodings are ordered, so: * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */ #define INTSET_ENC_INT16 (sizeof(int16_t)) #define INTSET_ENC_INT32 (sizeof(int32_t)) #define INTSET_ENC_INT64 (sizeof(int64_t)) /* Return the required encoding for the provided value. */ static uint8_t _intsetValueEncoding(int64_t v) { if (v < INT32_MIN || v > INT32_MAX) return INTSET_ENC_INT64; else if (v < INT16_MIN || v > INT16_MAX) return INTSET_ENC_INT32; else return INTSET_ENC_INT16; } /* Return the value at pos, given an encoding. */ static int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) { int64_t v64; int32_t v32; int16_t v16; if (enc == INTSET_ENC_INT64) { memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64)); //memrev64ifbe(&v64); return v64; } else if (enc == INTSET_ENC_INT32) { memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32)); //memrev32ifbe(&v32); return v32; } else { memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16)); //memrev16ifbe(&v16); return v16; } } /* Return the value at pos, using the configured encoding. */ static int64_t _intsetGet(intset *is, int pos) { return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding)); } /* Set the value at pos, using the configured encoding. */ static void _intsetSet(intset *is, int pos, int64_t value) { uint32_t encoding = intrev32ifbe(is->encoding); if (encoding == INTSET_ENC_INT64) { ((int64_t*)is->contents)[pos] = value; //memrev64ifbe(((int64_t*)is->contents)+pos); } else if (encoding == INTSET_ENC_INT32) { ((int32_t*)is->contents)[pos] = (int32_t)value; //memrev32ifbe(((int32_t*)is->contents)+pos); } else { ((int16_t*)is->contents)[pos] = (int16_t)value; //memrev16ifbe(((int16_t*)is->contents)+pos); } } /* Create an empty intset. */ intset *intsetNew(void) { intset *is = malloc(sizeof(intset)); is->encoding = intrev32ifbe(INTSET_ENC_INT16); is->length = 0; return is; } /* Resize the intset */ static intset *intsetResize(intset *is, uint32_t len) { uint32_t size = len*intrev32ifbe(is->encoding); is = realloc(is,sizeof(intset)+size); return is; } /* Search for the position of "value". Return 1 when the value was found and * sets "pos" to the position of the value within the intset. Return 0 when * the value is not present in the intset and sets "pos" to the position * where "value" can be inserted. */ static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) { int min = 0, max = intrev32ifbe(is->length)-1, mid = -1; int64_t cur = -1; /* The value can never be found when the set is empty */ if (intrev32ifbe(is->length) == 0) { if (pos) *pos = 0; return 0; } else { /* Check for the case where we know we cannot find the value, * but do know the insert position. */ if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) { if (pos) *pos = intrev32ifbe(is->length); return 0; } else if (value < _intsetGet(is,0)) { if (pos) *pos = 0; return 0; } } while(max >= min) { mid = (min+max)/2; cur = _intsetGet(is,mid); if (value > cur) { min = mid+1; } else if (value < cur) { max = mid-1; } else { break; } } if (value == cur) { if (pos) *pos = mid; return 1; } else { if (pos) *pos = min; return 0; } } /* Upgrades the intset to a larger encoding and inserts the given integer. */ static intset *intsetUpgradeAndAdd(intset *is, int64_t value) { uint8_t curenc = intrev32ifbe(is->encoding); uint8_t newenc = _intsetValueEncoding(value); int length = intrev32ifbe(is->length); int prepend = value < 0 ? 1 : 0; /* First set new encoding and resize */ is->encoding = intrev32ifbe(newenc); is = intsetResize(is,intrev32ifbe(is->length)+1); /* Upgrade back-to-front so we don't overwrite values. * Note that the "prepend" variable is used to make sure we have an empty * space at either the beginning or the end of the intset. */ while(length--) _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc)); /* Set the value at the beginning or the end. */ if (prepend) _intsetSet(is,0,value); else _intsetSet(is,intrev32ifbe(is->length),value); is->length = intrev32ifbe(intrev32ifbe(is->length)+1); return is; } static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) { void *src, *dst; uint32_t bytes = intrev32ifbe(is->length)-from; uint32_t encoding = intrev32ifbe(is->encoding); if (encoding == INTSET_ENC_INT64) { src = (int64_t*)is->contents+from; dst = (int64_t*)is->contents+to; bytes *= sizeof(int64_t); } else if (encoding == INTSET_ENC_INT32) { src = (int32_t*)is->contents+from; dst = (int32_t*)is->contents+to; bytes *= sizeof(int32_t); } else { src = (int16_t*)is->contents+from; dst = (int16_t*)is->contents+to; bytes *= sizeof(int16_t); } memmove(dst,src,bytes); } /* Insert an integer in the intset */ intset *intsetAdd(intset *is, int64_t value, uint8_t *success) { uint8_t valenc = _intsetValueEncoding(value); uint32_t pos; if (success) *success = 1; /* Upgrade encoding if necessary. If we need to upgrade, we know that * this value should be either appended (if > 0) or prepended (if < 0), * because it lies outside the range of existing values. */ if (valenc > intrev32ifbe(is->encoding)) { /* This always succeeds, so we don't need to curry *success. */ return intsetUpgradeAndAdd(is,value); } else { /* Abort if the value is already present in the set. * This call will populate "pos" with the right position to insert * the value when it cannot be found. */ if (intsetSearch(is,value,&pos)) { if (success) *success = 0; return is; } is = intsetResize(is,intrev32ifbe(is->length)+1); if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1); } _intsetSet(is,pos,value); is->length = intrev32ifbe(intrev32ifbe(is->length)+1); return is; } /* Delete integer from intset */ intset *intsetRemove(intset *is, int64_t value, int *success) { uint8_t valenc = _intsetValueEncoding(value); uint32_t pos; if (success) *success = 0; if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) { uint32_t len = intrev32ifbe(is->length); /* We know we can delete */ if (success) *success = 1; /* Overwrite value with tail and update length */ if (pos < (len-1)) intsetMoveTail(is,pos+1,pos); is = intsetResize(is,len-1); is->length = intrev32ifbe(len-1); } return is; } /* Determine whether a value belongs to this set */ uint8_t intsetFind(intset *is, int64_t value) { uint8_t valenc = _intsetValueEncoding(value); return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL); } /* Return random member */ int64_t intsetRandom(intset *is) { return _intsetGet(is,rand()%intrev32ifbe(is->length)); } /* Sets the value to the value at the given position. When this position is * out of range the function returns 0, when in range it returns 1. */ uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) { if (pos < intrev32ifbe(is->length)) { *value = _intsetGet(is,pos); return 1; } return 0; } /* Return intset length */ uint32_t intsetLen(intset *is) { return intrev32ifbe(is->length); } /* Return intset blob size in bytes. */ size_t intsetBlobLen(intset *is) { return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding); } #ifdef INTSET_TEST_MAIN #include void intsetRepr(intset *is) { int i; for (i = 0; i < intrev32ifbe(is->length); i++) { printf("%lld\n", (uint64_t)_intsetGet(is,i)); } printf("\n"); } void error(char *err) { printf("%s\n", err); exit(1); } void ok(void) { printf("OK\n"); } long long usec(void) { struct timeval tv; gettimeofday(&tv,NULL); return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; } #define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1))) void _assert(char *estr, char *file, int line) { printf("\n\n=== ASSERTION FAILED ===\n"); printf("==> %s:%d '%s' is not true\n",file,line,estr); } intset *createSet(int bits, int size) { uint64_t mask = (1< 32) { value = (rand()*rand()) & mask; } else { value = rand() & mask; } is = intsetAdd(is,value,NULL); } return is; } void checkConsistency(intset *is) { int i; for (i = 0; i < (intrev32ifbe(is->length)-1); i++) { uint32_t encoding = intrev32ifbe(is->encoding); if (encoding == INTSET_ENC_INT16) { int16_t *i16 = (int16_t*)is->contents; assert(i16[i] < i16[i+1]); } else if (encoding == INTSET_ENC_INT32) { int32_t *i32 = (int32_t*)is->contents; assert(i32[i] < i32[i+1]); } else { int64_t *i64 = (int64_t*)is->contents; assert(i64[i] < i64[i+1]); } } } int main(int argc, char **argv) { uint8_t success; int i; intset *is; sranddev(); printf("Value encodings: "); { assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16); assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16); assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32); assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32); assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32); assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32); assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64); assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64); assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64); assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64); ok(); } printf("Basic adding: "); { is = intsetNew(); is = intsetAdd(is,5,&success); assert(success); is = intsetAdd(is,6,&success); assert(success); is = intsetAdd(is,4,&success); assert(success); is = intsetAdd(is,4,&success); assert(!success); ok(); } printf("Large number of random adds: "); { int inserts = 0; is = intsetNew(); for (i = 0; i < 1024; i++) { is = intsetAdd(is,rand()%0x800,&success); if (success) inserts++; } assert(intrev32ifbe(is->length) == inserts); checkConsistency(is); ok(); } printf("Upgrade from int16 to int32: "); { is = intsetNew(); is = intsetAdd(is,32,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16); is = intsetAdd(is,65535,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32); assert(intsetFind(is,32)); assert(intsetFind(is,65535)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,32,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16); is = intsetAdd(is,-65535,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32); assert(intsetFind(is,32)); assert(intsetFind(is,-65535)); checkConsistency(is); ok(); } printf("Upgrade from int16 to int64: "); { is = intsetNew(); is = intsetAdd(is,32,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16); is = intsetAdd(is,4294967295,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64); assert(intsetFind(is,32)); assert(intsetFind(is,4294967295)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,32,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16); is = intsetAdd(is,-4294967295,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64); assert(intsetFind(is,32)); assert(intsetFind(is,-4294967295)); checkConsistency(is); ok(); } printf("Upgrade from int32 to int64: "); { is = intsetNew(); is = intsetAdd(is,65535,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32); is = intsetAdd(is,4294967295,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64); assert(intsetFind(is,65535)); assert(intsetFind(is,4294967295)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,65535,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32); is = intsetAdd(is,-4294967295,NULL); assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64); assert(intsetFind(is,65535)); assert(intsetFind(is,-4294967295)); checkConsistency(is); ok(); } printf("Stress lookups: "); { long num = 100000, size = 10000; int i, bits = 20; long long start; is = createSet(bits,size); checkConsistency(is); start = usec(); for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1< * Copyright (c) 2009-2012, Salvatore Sanfilippo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __INTSET_H #define __INTSET_H #include typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[]; } intset; intset *intsetNew(void); intset *intsetAdd(intset *is, int64_t value, uint8_t *success); intset *intsetRemove(intset *is, int64_t value, int *success); uint8_t intsetFind(intset *is, int64_t value); int64_t intsetRandom(intset *is); uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value); uint32_t intsetLen(intset *is); size_t intsetBlobLen(intset *is); #endif // __INTSET_H ================================================ FILE: QedisCore/redisZipList.c ================================================ /* The ziplist is a specially encoded dually linked list that is designed * to be very memory efficient. It stores both strings and integer values, * where integers are encoded as actual integers instead of a series of * characters. It allows push and pop operations on either side of the list * in O(1) time. However, because every operation requires a reallocation of * the memory used by the ziplist, the actual complexity is related to the * amount of memory used by the ziplist. * * ---------------------------------------------------------------------------- * * ZIPLIST OVERALL LAYOUT: * The general layout of the ziplist is as follows: * * * is an unsigned integer to hold the number of bytes that the * ziplist occupies. This value needs to be stored to be able to resize the * entire structure without the need to traverse it first. * * is the offset to the last entry in the list. This allows a pop * operation on the far side of the list without the need for full traversal. * * is the number of entries.When this value is larger than 2**16-2, * we need to traverse the entire list to know how many items it holds. * * is a single byte special value, equal to 255, which indicates the * end of the list. * * ZIPLIST ENTRIES: * Every entry in the ziplist is prefixed by a header that contains two pieces * of information. First, the length of the previous entry is stored to be * able to traverse the list from back to front. Second, the encoding with an * optional string length of the entry itself is stored. * * The length of the previous entry is encoded in the following way: * If this length is smaller than 254 bytes, it will only consume a single * byte that takes the length as value. When the length is greater than or * equal to 254, it will consume 5 bytes. The first byte is set to 254 to * indicate a larger value is following. The remaining 4 bytes take the * length of the previous entry as value. * * The other header field of the entry itself depends on the contents of the * entry. When the entry is a string, the first 2 bits of this header will hold * the type of encoding used to store the length of the string, followed by the * actual length of the string. When the entry is an integer the first 2 bits * are both set to 1. The following 2 bits are used to specify what kind of * integer will be stored after this header. An overview of the different * types and encodings is as follows: * * |00pppppp| - 1 byte * String value with length less than or equal to 63 bytes (6 bits). * |01pppppp|qqqqqqqq| - 2 bytes * String value with length less than or equal to 16383 bytes (14 bits). * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes * String value with length greater than or equal to 16384 bytes. * |11000000| - 1 byte * Integer encoded as int16_t (2 bytes). * |11010000| - 1 byte * Integer encoded as int32_t (4 bytes). * |11100000| - 1 byte * Integer encoded as int64_t (8 bytes). * |11110000| - 1 byte * Integer encoded as 24 bit signed (3 bytes). * |11111110| - 1 byte * Integer encoded as 8 bit signed (1 byte). * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer. * Unsigned integer from 0 to 12. The encoded value is actually from * 1 to 13 because 0000 and 1111 can not be used, so 1 should be * subtracted from the encoded 4 bit value to obtain the right value. * |11111111| - End of ziplist. * * All the integers are represented in little endian byte order. * * ---------------------------------------------------------------------------- * * Copyright (c) 2009-2012, Pieter Noordhuis * Copyright (c) 2009-2012, Salvatore Sanfilippo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "redisZipList.h" #define memrev32ifbe(x) (x) #define intrev32ifbe(x) (x) #define intrev16ifbe(x) (x) #define ZIP_END 255 #define ZIP_BIGLEN 254 /* Different encoding/length possibilities */ #define ZIP_STR_MASK 0xc0 #define ZIP_INT_MASK 0x30 #define ZIP_STR_06B (0 << 6) #define ZIP_STR_14B (1 << 6) #define ZIP_STR_32B (2 << 6) #define ZIP_INT_16B (0xc0 | 0<<4) #define ZIP_INT_32B (0xc0 | 1<<4) #define ZIP_INT_64B (0xc0 | 2<<4) #define ZIP_INT_24B (0xc0 | 3<<4) #define ZIP_INT_8B 0xfe /* 4 bit integer immediate encoding */ #define ZIP_INT_IMM_MASK 0x0f #define ZIP_INT_IMM_MIN 0xf1 /* 11110001 */ #define ZIP_INT_IMM_MAX 0xfd /* 11111101 */ #define ZIP_INT_IMM_VAL(v) (v & ZIP_INT_IMM_MASK) #define INT24_MAX 0x7fffff #define INT24_MIN (-INT24_MAX - 1) /* Macro to determine type */ #define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK) /* Utility macros */ #define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl))) #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t)))) #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2))) #define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t)) #define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE) #define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) #define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1) /* We know a positive increment can only be 1 because entries can only be * pushed one at a time. */ #define ZIPLIST_INCR_LENGTH(zl,incr) { \ if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \ ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \ } typedef struct zlentry { unsigned int prevrawlensize, prevrawlen; unsigned int lensize, len; unsigned int headersize; unsigned char encoding; unsigned char *p; } zlentry; /* Extract the encoding from the byte pointed by 'ptr' and set it into * 'encoding'. */ #define ZIP_ENTRY_ENCODING(ptr, encoding) do { \ (encoding) = (ptr[0]); \ if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \ } while(0) /* Return bytes needed to store integer encoded by 'encoding' */ static unsigned int zipIntSize(unsigned char encoding) { switch(encoding) { case ZIP_INT_8B: return 1; case ZIP_INT_16B: return 2; case ZIP_INT_24B: return 3; case ZIP_INT_32B: return 4; case ZIP_INT_64B: return 8; default: return 0; /* 4 bit immediate */ } assert(NULL); return 0; } /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns * the amount of bytes required to encode such a length. */ static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) { unsigned char len = 1, buf[5]; if (ZIP_IS_STR(encoding)) { /* Although encoding is given it may not be set for strings, * so we determine it here using the raw length. */ if (rawlen <= 0x3f) { if (!p) return len; buf[0] = ZIP_STR_06B | rawlen; } else if (rawlen <= 0x3fff) { len += 1; if (!p) return len; buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f); buf[1] = rawlen & 0xff; } else { len += 4; if (!p) return len; buf[0] = ZIP_STR_32B; buf[1] = (rawlen >> 24) & 0xff; buf[2] = (rawlen >> 16) & 0xff; buf[3] = (rawlen >> 8) & 0xff; buf[4] = rawlen & 0xff; } } else { /* Implies integer encoding, so length is always 1. */ if (!p) return len; buf[0] = encoding; } /* Store this length at p */ memcpy(p,buf,len); return len; } /* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the * entries encoding, the 'lensize' variable will hold the number of bytes * required to encode the entries length, and the 'len' variable will hold the * entries length. */ #define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \ ZIP_ENTRY_ENCODING((ptr), (encoding)); \ if ((encoding) < ZIP_STR_MASK) { \ if ((encoding) == ZIP_STR_06B) { \ (lensize) = 1; \ (len) = (ptr)[0] & 0x3f; \ } else if ((encoding) == ZIP_STR_14B) { \ (lensize) = 2; \ (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \ } else if (encoding == ZIP_STR_32B) { \ (lensize) = 5; \ (len) = ((ptr)[1] << 24) | \ ((ptr)[2] << 16) | \ ((ptr)[3] << 8) | \ ((ptr)[4]); \ } else { \ assert(NULL); \ } \ } else { \ (lensize) = 1; \ (len) = zipIntSize(encoding); \ } \ } while(0); /* Encode the length of the previous entry and write it to "p". Return the * number of bytes needed to encode this length if "p" is NULL. */ static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) { if (p == NULL) { return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1; } else { if (len < ZIP_BIGLEN) { p[0] = len; return 1; } else { p[0] = ZIP_BIGLEN; memcpy(p+1,&len,sizeof(len)); //memrev32ifbe(p+1); return 1+sizeof(len); } } } /* Encode the length of the previous entry and write it to "p". This only * uses the larger encoding (required in __ziplistCascadeUpdate). */ static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) { if (p == NULL) return; p[0] = ZIP_BIGLEN; memcpy(p+1,&len,sizeof(len)); // memrev32ifbe(p+1); } /* Decode the number of bytes required to store the length of the previous * element, from the perspective of the entry pointed to by 'ptr'. */ #define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \ if ((ptr)[0] < ZIP_BIGLEN) { \ (prevlensize) = 1; \ } else { \ (prevlensize) = 5; \ } \ } while(0); /* Decode the length of the previous element, from the perspective of the entry * pointed to by 'ptr'. */ #define ZIP_DECODE_PREVLEN(ptr, prevlensize, prevlen) do { \ ZIP_DECODE_PREVLENSIZE(ptr, prevlensize); \ if ((prevlensize) == 1) { \ (prevlen) = (ptr)[0]; \ } else if ((prevlensize) == 5) { \ assert(sizeof((prevlensize)) == 4); \ memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \ } \ } while(0); /* Return the difference in number of bytes needed to store the length of the * previous element 'len', in the entry pointed to by 'p'. */ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { unsigned int prevlensize; ZIP_DECODE_PREVLENSIZE(p, prevlensize); return zipPrevEncodeLength(NULL, len) - prevlensize; } /* Return the total number of bytes used by the entry pointed to by 'p'. */ static unsigned int zipRawEntryLength(unsigned char *p) { unsigned int prevlensize, encoding, lensize, len; ZIP_DECODE_PREVLENSIZE(p, prevlensize); ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); return prevlensize + lensize + len; } int Strtoll(const char* ptr, size_t nBytes, long long* outVal) { if (nBytes == 0 || nBytes > 21) return 0; char* pEnd = 0; long long ret = strtoll(ptr, &pEnd, 0); *outVal = ret; return pEnd == ptr + nBytes; } /* Check if string pointed to by 'entry' can be encoded as an integer. * Stores the integer value in 'v' and its encoding in 'encoding'. */ static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) { long long value; if (entrylen >= 32 || entrylen == 0) return 0; if (Strtoll((const char*)entry, entrylen, &value)) { /* Great, the string can be encoded. Check what's the smallest * of our encoding types that can hold this value. */ if (value >= 0 && value <= 12) { *encoding = ZIP_INT_IMM_MIN+value; } else if (value >= INT8_MIN && value <= INT8_MAX) { *encoding = ZIP_INT_8B; } else if (value >= INT16_MIN && value <= INT16_MAX) { *encoding = ZIP_INT_16B; } else if (value >= INT24_MIN && value <= INT24_MAX) { *encoding = ZIP_INT_24B; } else if (value >= INT32_MIN && value <= INT32_MAX) { *encoding = ZIP_INT_32B; } else { *encoding = ZIP_INT_64B; } *v = value; return 1; } return 0; } /* Store integer 'value' at 'p', encoded as 'encoding' */ static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) { int16_t i16; int32_t i32; int64_t i64; if (encoding == ZIP_INT_8B) { ((int8_t*)p)[0] = (int8_t)value; } else if (encoding == ZIP_INT_16B) { i16 = value; memcpy(p,&i16,sizeof(i16)); //memrev16ifbe(p); } else if (encoding == ZIP_INT_24B) { i32 = (int32_t)(value<<8); //memrev32ifbe(&i32); memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t)); } else if (encoding == ZIP_INT_32B) { i32 = (int32_t)(value); memcpy(p,&i32,sizeof(i32)); // memrev32ifbe(p); } else if (encoding == ZIP_INT_64B) { i64 = value; memcpy(p,&i64,sizeof(i64)); //memrev64ifbe(p); } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) { /* Nothing to do, the value is stored in the encoding itself. */ } else { assert(NULL); } } /* Read integer encoded as 'encoding' from 'p' */ static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) { int16_t i16; int32_t i32; int64_t i64, ret = 0; if (encoding == ZIP_INT_8B) { ret = ((int8_t*)p)[0]; } else if (encoding == ZIP_INT_16B) { memcpy(&i16,p,sizeof(i16)); // memrev16ifbe(&i16); ret = i16; } else if (encoding == ZIP_INT_32B) { memcpy(&i32,p,sizeof(i32)); // memrev32ifbe(&i32); ret = i32; } else if (encoding == ZIP_INT_24B) { i32 = 0; memcpy(((uint8_t*)&i32)+1,p,sizeof(i32)-sizeof(uint8_t)); // memrev32ifbe(&i32); ret = i32>>8; } else if (encoding == ZIP_INT_64B) { memcpy(&i64,p,sizeof(i64)); // memrev64ifbe(&i64); ret = i64; } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) { ret = (encoding & ZIP_INT_IMM_MASK)-1; } else { assert(NULL); } return ret; } /* Return a struct with all information about an entry. */ static zlentry zipEntry(unsigned char *p) { zlentry e; ZIP_DECODE_PREVLEN(p, e.prevrawlensize, e.prevrawlen); ZIP_DECODE_LENGTH(p + e.prevrawlensize, e.encoding, e.lensize, e.len); e.headersize = e.prevrawlensize + e.lensize; e.p = p; return e; } /* Create a new empty ziplist. */ unsigned char *ziplistNew(void) { unsigned int bytes = ZIPLIST_HEADER_SIZE+1; unsigned char *zl = (unsigned char* )malloc(bytes); ZIPLIST_BYTES(zl) = intrev32ifbe(bytes); ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE); ZIPLIST_LENGTH(zl) = 0; zl[bytes-1] = ZIP_END; return zl; } /* Resize the ziplist. */ static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { zl = (unsigned char* )realloc(zl,len); ZIPLIST_BYTES(zl) = intrev32ifbe(len); zl[len-1] = ZIP_END; return zl; } /* When an entry is inserted, we need to set the prevlen field of the next * entry to equal the length of the inserted entry. It can occur that this * length cannot be encoded in 1 byte and the next entry needs to be grow * a bit larger to hold the 5-byte encoded prevlen. This can be done for free, * because this only happens when an entry is already being inserted (which * causes a realloc and memmove). However, encoding the prevlen may require * that this entry is grown as well. This effect may cascade throughout * the ziplist when there are consecutive entries with a size close to * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every * consecutive entry. * * Note that this effect can also happen in reverse, where the bytes required * to encode the prevlen field can shrink. This effect is deliberately ignored, * because it can cause a "flapping" effect where a chain prevlen fields is * first grown and then shrunk again after consecutive inserts. Rather, the * field is allowed to stay larger than necessary, because a large prevlen * field implies the ziplist is holding large entries anyway. * * The pointer "p" points to the first entry that does NOT need to be * updated, i.e. consecutive fields MAY need an update. */ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) { size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize; size_t offset, noffset, extra; unsigned char *np; zlentry cur, next; while (p[0] != ZIP_END) { cur = zipEntry(p); rawlen = cur.headersize + cur.len; rawlensize = zipPrevEncodeLength(NULL,rawlen); /* Abort if there is no next entry. */ if (p[rawlen] == ZIP_END) break; next = zipEntry(p+rawlen); /* Abort when "prevlen" has not changed. */ if (next.prevrawlen == rawlen) break; if (next.prevrawlensize < rawlensize) { /* The "prevlen" field of "next" needs more bytes to hold * the raw length of "cur". */ offset = p-zl; extra = rawlensize-next.prevrawlensize; zl = ziplistResize(zl,curlen+extra); p = zl+offset; /* Current pointer and offset for next element. */ np = p+rawlen; noffset = np-zl; /* Update tail offset when next element is not the tail element. */ if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra); } /* Move the tail to the back. */ memmove(np+rawlensize, np+next.prevrawlensize, curlen-noffset-next.prevrawlensize-1); zipPrevEncodeLength(np,rawlen); /* Advance the cursor */ p += rawlen; curlen += extra; } else { if (next.prevrawlensize > rawlensize) { /* This would result in shrinking, which we want to avoid. * So, set "rawlen" in the available bytes. */ zipPrevEncodeLengthForceLarge(p+rawlen,rawlen); } else { zipPrevEncodeLength(p+rawlen,rawlen); } /* Stop here, as the raw length of "next" has not changed. */ break; } } return zl; } /* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) { unsigned int i, totlen, deleted = 0; size_t offset; int nextdiff = 0; zlentry first, tail; first = zipEntry(p); for (i = 0; p[0] != ZIP_END && i < num; i++) { p += zipRawEntryLength(p); deleted++; } totlen = p-first.p; if (totlen > 0) { if (p[0] != ZIP_END) { /* Storing `prevrawlen` in this entry may increase or decrease the * number of bytes required compare to the current `prevrawlen`. * There always is room to store this, because it was previously * stored by an entry that is now being deleted. */ nextdiff = zipPrevLenByteDiff(p,first.prevrawlen); p -= nextdiff; zipPrevEncodeLength(p,first.prevrawlen); /* Update offset for tail */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen); /* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ tail = zipEntry(p); if (p[tail.headersize+tail.len] != ZIP_END) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); } /* Move tail to the front of the ziplist */ memmove(first.p,p, intrev32ifbe(ZIPLIST_BYTES(zl))-(p-zl)-1); } else { /* The entire tail was deleted. No need to move memory. */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe((first.p-zl)-first.prevrawlen); } /* Resize and update length */ offset = first.p-zl; zl = ziplistResize(zl, intrev32ifbe(ZIPLIST_BYTES(zl))-totlen+nextdiff); ZIPLIST_INCR_LENGTH(zl,-deleted); p = zl+offset; /* When nextdiff != 0, the raw length of the next entry has changed, so * we need to cascade the update throughout the ziplist */ if (nextdiff != 0) zl = __ziplistCascadeUpdate(zl,p); } return zl; } /* Insert item at "p". */ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0; size_t offset; int nextdiff = 0; unsigned char encoding = 0; long long value = 123456789; /* initialized to avoid warning. Using a value that is easy to see if for some reason we use it uninitialized. */ zlentry entry, tail; /* Find out prevlen for the entry that is inserted. */ if (p[0] != ZIP_END) { entry = zipEntry(p); prevlen = entry.prevrawlen; } else { unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl); if (ptail[0] != ZIP_END) { prevlen = zipRawEntryLength(ptail); } } /* See if the entry can be encoded */ if (zipTryEncoding(s,slen,&value,&encoding)) { /* 'encoding' is set to the appropriate integer encoding */ reqlen = zipIntSize(encoding); } else { /* 'encoding' is untouched, however zipEncodeLength will use the * string length to figure out how to encode it. */ reqlen = slen; } /* We need space for both the length of the previous entry and * the length of the payload. */ reqlen += zipPrevEncodeLength(NULL,prevlen); reqlen += zipEncodeLength(NULL,encoding,slen); /* When the insert position is not equal to the tail, we need to * make sure that the next entry can hold this entry's length in * its prevlen field. */ nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0; /* Store offset because a realloc may change the address of zl. */ offset = p-zl; zl = ziplistResize(zl,curlen+reqlen+nextdiff); p = zl+offset; /* Apply memory move when necessary and update tail offset. */ if (p[0] != ZIP_END) { /* Subtract one because of the ZIP_END bytes */ memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff); /* Encode this entry's raw length in the next entry. */ zipPrevEncodeLength(p+reqlen,reqlen); /* Update offset for tail */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen); /* When the tail contains more than one entry, we need to take * "nextdiff" in account as well. Otherwise, a change in the * size of prevlen doesn't have an effect on the *tail* offset. */ tail = zipEntry(p+reqlen); if (p[reqlen+tail.headersize+tail.len] != ZIP_END) { ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff); } } else { /* This element will be the new tail. */ ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl); } /* When nextdiff != 0, the raw length of the next entry has changed, so * we need to cascade the update throughout the ziplist */ if (nextdiff != 0) { offset = p-zl; zl = __ziplistCascadeUpdate(zl,p+reqlen); p = zl+offset; } /* Write the entry */ p += zipPrevEncodeLength(p,prevlen); p += zipEncodeLength(p,encoding,slen); if (ZIP_IS_STR(encoding)) { memcpy(p,s,slen); } else { zipSaveInteger(p,value,encoding); } ZIPLIST_INCR_LENGTH(zl,1); return zl; } unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) { unsigned char *p; p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl); return __ziplistInsert(zl,p,s,slen); } /* Returns an offset to use for iterating with ziplistNext. When the given * index is negative, the list is traversed back to front. When the list * doesn't contain an element at the provided index, NULL is returned. */ unsigned char *ziplistIndex(unsigned char *zl, int index) { unsigned char *p; zlentry entry; if (index < 0) { index = (-index)-1; p = ZIPLIST_ENTRY_TAIL(zl); if (p[0] != ZIP_END) { entry = zipEntry(p); while (entry.prevrawlen > 0 && index--) { p -= entry.prevrawlen; entry = zipEntry(p); } } } else { p = ZIPLIST_ENTRY_HEAD(zl); while (p[0] != ZIP_END && index--) { p += zipRawEntryLength(p); } } return (p[0] == ZIP_END || index > 0) ? NULL : p; } /* Return pointer to next entry in ziplist. * * zl is the pointer to the ziplist * p is the pointer to the current element * * The element after 'p' is returned, otherwise NULL if we are at the end. */ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) { ((void) zl); /* "p" could be equal to ZIP_END, caused by ziplistDelete, * and we should return NULL. Otherwise, we should return NULL * when the *next* element is ZIP_END (there is no next entry). */ if (p[0] == ZIP_END) { return NULL; } p += zipRawEntryLength(p); if (p[0] == ZIP_END) { return NULL; } return p; } /* Return pointer to previous entry in ziplist. */ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) { zlentry entry; /* Iterating backwards from ZIP_END should return the tail. When "p" is * equal to the first element of the list, we're already at the head, * and should return NULL. */ if (p[0] == ZIP_END) { p = ZIPLIST_ENTRY_TAIL(zl); return (p[0] == ZIP_END) ? NULL : p; } else if (p == ZIPLIST_ENTRY_HEAD(zl)) { return NULL; } else { entry = zipEntry(p); assert(entry.prevrawlen > 0); return p-entry.prevrawlen; } } /* Get entry pointed to by 'p' and store in either 'e' or 'v' depending * on the encoding of the entry. 'e' is always set to NULL to be able * to find out whether the string pointer or the integer value was set. * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */ unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) { zlentry entry; if (p == NULL || p[0] == ZIP_END) return 0; if (sstr) *sstr = NULL; entry = zipEntry(p); if (ZIP_IS_STR(entry.encoding)) { if (sstr) { *slen = entry.len; *sstr = p+entry.headersize; } } else { if (sval) { *sval = zipLoadInteger(p+entry.headersize,entry.encoding); } } return 1; } /* Insert an entry at "p". */ unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) { return __ziplistInsert(zl,p,s,slen); } /* Delete a single entry from the ziplist, pointed to by *p. * Also update *p in place, to be able to iterate over the * ziplist, while deleting entries. */ unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) { size_t offset = *p-zl; zl = __ziplistDelete(zl,*p,1); /* Store pointer to current element in p, because ziplistDelete will * do a realloc which might result in a different "zl"-pointer. * When the delete direction is back to front, we might delete the last * entry and end up with "p" pointing to ZIP_END, so check this. */ *p = zl+offset; return zl; } /* Delete a range of entries from the ziplist. */ unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num) { unsigned char *p = ziplistIndex(zl,index); return (p == NULL) ? zl : __ziplistDelete(zl,p,num); } /* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) { zlentry entry; unsigned char sencoding; long long zval, sval; if (p[0] == ZIP_END) return 0; entry = zipEntry(p); if (ZIP_IS_STR(entry.encoding)) { /* Raw compare */ if (entry.len == slen) { return memcmp(p+entry.headersize,sstr,slen) == 0; } else { return 0; } } else { /* Try to compare encoded values. Don't compare encoding because * different implementations may encoded integers differently. */ if (zipTryEncoding(sstr,slen,&sval,&sencoding)) { zval = zipLoadInteger(p+entry.headersize,entry.encoding); return zval == sval; } } return 0; } /* Find pointer to the entry equal to the specified entry. Skip 'skip' entries * between every comparison. Returns NULL when the field could not be found. */ unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) { int skipcnt = 0; unsigned char vencoding = 0; long long vll = 0; while (p[0] != ZIP_END) { unsigned int prevlensize, encoding, lensize, len; unsigned char *q; ZIP_DECODE_PREVLENSIZE(p, prevlensize); ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); q = p + prevlensize + lensize; if (skipcnt == 0) { /* Compare current entry with specified entry */ if (ZIP_IS_STR(encoding)) { if (len == vlen && memcmp(q, vstr, vlen) == 0) { return p; } } else { /* Find out if the searched field can be encoded. Note that * we do it only the first time, once done vencoding is set * to non-zero and vll is set to the integer value. */ if (vencoding == 0) { if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) { /* If the entry can't be encoded we set it to * UCHAR_MAX so that we don't retry again the next * time. */ vencoding = UCHAR_MAX; } /* Must be non-zero by now */ assert(vencoding); } /* Compare current entry with specified entry, do it only * if vencoding != UCHAR_MAX because if there is no encoding * possible for the field it can't be a valid integer. */ if (vencoding != UCHAR_MAX) { long long ll = zipLoadInteger(q, encoding); if (ll == vll) { return p; } } } /* Reset skip count */ skipcnt = skip; } else { /* Skip entry */ skipcnt--; } /* Move to next entry */ p = q + len; } return NULL; } /* Return length of ziplist. */ unsigned int ziplistLen(unsigned char *zl) { unsigned int len = 0; if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) { len = intrev16ifbe(ZIPLIST_LENGTH(zl)); } else { unsigned char *p = zl+ZIPLIST_HEADER_SIZE; while (*p != ZIP_END) { p += zipRawEntryLength(p); len++; } /* Re-store length if small enough */ if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len); } return len; } /* Return ziplist blob size in bytes. */ size_t ziplistBlobLen(unsigned char *zl) { return intrev32ifbe(ZIPLIST_BYTES(zl)); } void ziplistRepr(unsigned char *zl) { unsigned char *p; int index = 0; zlentry entry; printf( "{total bytes %d} " "{length %u}\n" "{tail offset %u}\n", intrev32ifbe(ZIPLIST_BYTES(zl)), intrev16ifbe(ZIPLIST_LENGTH(zl)), intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))); p = ZIPLIST_ENTRY_HEAD(zl); while(*p != ZIP_END) { entry = zipEntry(p); printf( "{" "addr 0x%08lx, " "index %2d, " "offset %5ld, " "rl: %5u, " "hs %2u, " "pl: %5u, " "pls: %2u, " "payload %5u" "} ", (long unsigned)p, index, (unsigned long) (p-zl), entry.headersize+entry.len, entry.headersize, entry.prevrawlen, entry.prevrawlensize, entry.len); p += entry.headersize; if (ZIP_IS_STR(entry.encoding)) { if (entry.len > 40) { if (fwrite(p,40,1,stdout) == 0) perror("fwrite"); printf("..."); } else { if (entry.len && fwrite(p,entry.len,1,stdout) == 0) perror("fwrite"); } } else { printf("%lld", (long long) zipLoadInteger(p,entry.encoding)); } printf("\n"); p += entry.len; index++; } printf("{end}\n\n"); } #ifdef ZIPLIST_TEST_MAIN #include #include "adlist.h" #include "sds.h" #define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); } unsigned char *createList() { unsigned char *zl = ziplistNew(); zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL); zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL); zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_HEAD); zl = ziplistPush(zl, (unsigned char*)"1024", 4, ZIPLIST_TAIL); return zl; } unsigned char *createIntList() { unsigned char *zl = ziplistNew(); char buf[32]; sprintf(buf, "100"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); sprintf(buf, "128000"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); sprintf(buf, "-100"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD); sprintf(buf, "4294967296"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD); sprintf(buf, "non integer"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); sprintf(buf, "much much longer non integer"); zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL); return zl; } long long usec(void) { struct timeval tv; gettimeofday(&tv,NULL); return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; } void stress(int pos, int num, int maxsize, int dnum) { int i,j,k; unsigned char *zl; char posstr[2][5] = { "HEAD", "TAIL" }; long long start; for (i = 0; i < maxsize; i+=dnum) { zl = ziplistNew(); for (j = 0; j < i; j++) { zl = ziplistPush(zl,(unsigned char*)"quux",4,ZIPLIST_TAIL); } /* Do num times a push+pop from pos */ start = usec(); for (k = 0; k < num; k++) { zl = ziplistPush(zl,(unsigned char*)"quux",4,pos); zl = ziplistDeleteRange(zl,0,1); } printf("List size: %8d, bytes: %8d, %dx push+pop (%s): %6lld usec\n", i,intrev32ifbe(ZIPLIST_BYTES(zl)),num,posstr[pos],usec()-start); zfree(zl); } } void pop(unsigned char *zl, int where) { unsigned char *p, *vstr; unsigned int vlen; long long vlong; p = ziplistIndex(zl,where == ZIPLIST_HEAD ? 0 : -1); if (ziplistGet(p,&vstr,&vlen,&vlong)) { if (where == ZIPLIST_HEAD) printf("Pop head: "); else printf("Pop tail: "); if (vstr) if (vlen && fwrite(vstr,vlen,1,stdout) == 0) perror("fwrite"); else printf("%lld", vlong); printf("\n"); ziplistDeleteRange(zl,-1,1); } else { printf("ERROR: Could not pop\n"); exit(1); } } int randstring(char *target, unsigned int min, unsigned int max) { int p, len = min+rand()%(max-min+1); int minval, maxval; switch(rand() % 3) { case 0: minval = 0; maxval = 255; break; case 1: minval = 48; maxval = 122; break; case 2: minval = 48; maxval = 52; break; default: assert(NULL); } while(p < len) target[p++] = minval+rand()%(maxval-minval+1); return len; } void verify(unsigned char *zl, zlentry *e) { int i; int len = ziplistLen(zl); zlentry _e; for (i = 0; i < len; i++) { memset(&e[i], 0, sizeof(zlentry)); e[i] = zipEntry(ziplistIndex(zl, i)); memset(&_e, 0, sizeof(zlentry)); _e = zipEntry(ziplistIndex(zl, -len+i)); assert(memcmp(&e[i], &_e, sizeof(zlentry)) == 0); } } int main(int argc, char **argv) { unsigned char *zl, *p; unsigned char *entry; unsigned int elen; long long value; /* If an argument is given, use it as the random seed. */ if (argc == 2) srand(atoi(argv[1])); zl = createIntList(); ziplistRepr(zl); zl = createList(); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_HEAD); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); printf("Get element at index 3:\n"); { zl = createList(); p = ziplistIndex(zl, 3); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index 3\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index 4 (out of range):\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Get element at index -1 (last element):\n"); { zl = createList(); p = ziplistIndex(zl, -1); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -1\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -4 (first element):\n"); { zl = createList(); p = ziplistIndex(zl, -4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -4\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -5 (reverse out of range):\n"); { zl = createList(); p = ziplistIndex(zl, -5); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Iterate list from 0 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 0); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 1 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 2 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 2); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate starting out of range:\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("No entry\n"); } else { printf("ERROR\n"); } printf("\n"); } printf("Iterate from back to front:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Iterate from back to front, deleting all items:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } zl = ziplistDelete(zl,&p); p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Delete inclusive range 0,0:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 1); ziplistRepr(zl); } printf("Delete inclusive range 0,1:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 2); ziplistRepr(zl); } printf("Delete inclusive range 1,2:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 2); ziplistRepr(zl); } printf("Delete with start index out of range:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 5, 1); ziplistRepr(zl); } printf("Delete with num overflow:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 5); ziplistRepr(zl); } printf("Delete foo while iterating:\n"); { zl = createList(); p = ziplistIndex(zl,0); while (ziplistGet(p,&entry,&elen,&value)) { if (entry && strncmp("foo",(char*)entry,elen) == 0) { printf("Delete foo\n"); zl = ziplistDelete(zl,&p); } else { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld",value); } p = ziplistNext(zl,p); printf("\n"); } } printf("\n"); ziplistRepr(zl); } printf("Regression test for >255 byte strings:\n"); { char v1[257],v2[257]; memset(v1,'x',256); memset(v2,'y',256); zl = ziplistNew(); zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL); zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL); /* Pop values again and compare their value. */ p = ziplistIndex(zl,0); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v1,(char*)entry,elen) == 0); p = ziplistIndex(zl,1); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v2,(char*)entry,elen) == 0); printf("SUCCESS\n\n"); } printf("Regression test deleting next to last entries:\n"); { char v[3][257]; zlentry e[3]; int i; for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { memset(v[i], 'a' + i, sizeof(v[0])); } v[0][256] = '\0'; v[1][ 1] = '\0'; v[2][256] = '\0'; zl = ziplistNew(); for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL); } verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); assert(e[2].prevrawlensize == 1); /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */ unsigned char *p = e[1].p; zl = ziplistDelete(zl, &p); verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); printf("SUCCESS\n\n"); } printf("Create long list and check indices:\n"); { zl = ziplistNew(); char buf[32]; int i,len; for (i = 0; i < 1000; i++) { len = sprintf(buf,"%d",i); zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL); } for (i = 0; i < 1000; i++) { p = ziplistIndex(zl,i); assert(ziplistGet(p,NULL,NULL,&value)); assert(i == value); p = ziplistIndex(zl,-i-1); assert(ziplistGet(p,NULL,NULL,&value)); assert(999-i == value); } printf("SUCCESS\n\n"); } printf("Compare strings with ziplist entries:\n"); { zl = createList(); p = ziplistIndex(zl,0); if (!ziplistCompare(p,(unsigned char*)"hello",5)) { printf("ERROR: not \"hello\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"hella",5)) { printf("ERROR: \"hella\"\n"); return 1; } p = ziplistIndex(zl,3); if (!ziplistCompare(p,(unsigned char*)"1024",4)) { printf("ERROR: not \"1024\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"1025",4)) { printf("ERROR: \"1025\"\n"); return 1; } printf("SUCCESS\n\n"); } printf("Stress with random payloads of different encoding:\n"); { int i,j,len,where; unsigned char *p; char buf[1024]; int buflen; list *ref; listNode *refnode; /* Hold temp vars from ziplist */ unsigned char *sstr; unsigned int slen; long long sval; for (i = 0; i < 20000; i++) { zl = ziplistNew(); ref = listCreate(); listSetFreeMethod(ref,sdsfree); len = rand() % 256; /* Create lists */ for (j = 0; j < len; j++) { where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL; if (rand() % 2) { buflen = randstring(buf,1,sizeof(buf)-1); } else { switch(rand() % 3) { case 0: buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20); break; case 1: buflen = sprintf(buf,"%lld",(0LL + rand())); break; case 2: buflen = sprintf(buf,"%lld",(0LL + rand()) << 20); break; default: assert(NULL); } } /* Add to ziplist */ zl = ziplistPush(zl, (unsigned char*)buf, buflen, where); /* Add to reference list */ if (where == ZIPLIST_HEAD) { listAddNodeHead(ref,sdsnewlen(buf, buflen)); } else if (where == ZIPLIST_TAIL) { listAddNodeTail(ref,sdsnewlen(buf, buflen)); } else { assert(NULL); } } assert(listLength(ref) == ziplistLen(zl)); for (j = 0; j < len; j++) { /* Naive way to get elements, but similar to the stresser * executed from the Tcl test suite. */ p = ziplistIndex(zl,j); refnode = listIndex(ref,j); assert(ziplistGet(p,&sstr,&slen,&sval)); if (sstr == NULL) { buflen = sprintf(buf,"%lld",sval); } else { buflen = slen; memcpy(buf,sstr,buflen); buf[buflen] = '\0'; } assert(memcmp(buf,listNodeValue(refnode),buflen) == 0); } zfree(zl); listRelease(ref); } printf("SUCCESS\n\n"); } printf("Stress with variable ziplist size:\n"); { stress(ZIPLIST_HEAD,100000,16384,256); stress(ZIPLIST_TAIL,100000,16384,256); } return 0; } #endif ================================================ FILE: QedisCore/redisZipList.h ================================================ /* * Copyright (c) 2009-2012, Pieter Noordhuis * Copyright (c) 2009-2012, Salvatore Sanfilippo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #define ZIPLIST_HEAD 0 #define ZIPLIST_TAIL 1 unsigned char *ziplistNew(void); unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where); unsigned char *ziplistIndex(unsigned char *zl, int index); unsigned char *ziplistNext(unsigned char *zl, unsigned char *p); unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p); unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval); unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen); unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p); unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num); unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen); unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip); unsigned int ziplistLen(unsigned char *zl); size_t ziplistBlobLen(unsigned char *zl); ================================================ FILE: QedisSvr/CMakeLists.txt ================================================ #PROJECT(QEDISSERVER) INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) AUX_SOURCE_DIRECTORY(. QEDISSERVER_SRC) LINK_DIRECTORIES(../../leveldb) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase; ${PROJECT_SOURCE_DIR}/QedisCore) ADD_EXECUTABLE(qedis_server ${QEDISSERVER_SRC}) SET(EXECUTABLE_OUTPUT_PATH ../../bin) TARGET_LINK_LIBRARIES(qedis_server qediscore; qbaselib; leveldb) ADD_DEPENDENCIES(qedis_server qbaselib; qediscore; leveldb) IF(${QEDIS_CLUSTER} EQUAL 1) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QSentinel) TARGET_LINK_LIBRARIES(qedis_server qcluster) ADD_DEPENDENCIES(qedis_server qcluster) ENDIF() ================================================ FILE: QedisSvr/Qedis.cc ================================================ // // Qedis.cc // // Copyright (c) 2014-2018 Bert Young. All rights reserved. // #include #include #include #include "Log/Logger.h" #include "Timer.h" #include "QClient.h" #include "QSlaveClient.h" #include "QStore.h" #include "QCommand.h" #include "QPubsub.h" #include "QMigration.h" #include "QDB.h" #include "QAOF.h" #include "QConfig.h" #include "QSlowLog.h" #include "QModule.h" #include "QedisLogo.h" #include "Qedis.h" #if QEDIS_CLUSTER #include "QClusterClient.h" #endif const unsigned Qedis::kRunidSize = 40; Qedis::Qedis() : port_(0), masterPort_(0) { } Qedis::~Qedis() { } static void Usage() { std::cerr << "Usage: ./qedis-server [/path/to/redis.conf] [options]\n\ ./qedis-server -v or --version\n\ ./qedis-server -h or --help\n\ Examples:\n\ ./qedis-server (run the server with default conf)\n\ ./qedis-server /etc/redis/6379.conf\n\ ./qedis-server --port 7777\n\ ./qedis-server --port 7777 --slaveof 127.0.0.1 8888\n\ ./qedis-server /etc/myredis.conf --loglevel verbose\n"; } bool Qedis::ParseArgs(int ac, char* av[]) { for (int i = 0; i < ac; i ++) { if (cfgFile_.empty() && ::access(av[i], R_OK) == 0) { cfgFile_ = av[i]; continue; } else if (strncasecmp(av[i], "-v", 2) == 0 || strncasecmp(av[i], "--version", 9) == 0) { std::cerr << "Qedis Server v=" << QEDIS_VERSION << " bits=" << (sizeof(void*) == 8 ? 64 : 32) << std::endl; exit(0); return true; } else if (strncasecmp(av[i], "-h", 2) == 0 || strncasecmp(av[i], "--help", 6) == 0) { Usage(); exit(0); return true; } else if (strncasecmp(av[i], "--port", 6) == 0) { if (++i == ac) { return false; } port_ = static_cast(std::atoi(av[i])); } else if (strncasecmp(av[i], "--loglevel", 10) == 0) { if (++i == ac) { return false; } logLevel_ = std::string(av[i]); } else if (strncasecmp(av[i], "--slaveof", 9) == 0) { if (i + 2 >= ac) { return false; } master_ = std::string(av[++i]); masterPort_ = static_cast(std::atoi(av[++i])); } else { std::cerr << "Unknow option " << av[i] << std::endl; return false; } } return true; } std::shared_ptr Qedis::_OnNewConnection(int connfd, int tag) { using namespace qedis; SocketAddr peer; Socket::GetPeerAddr(connfd, peer); switch (tag) { case ConnectionTag::kQedisClient: { // incoming clients or connect to master auto cli(std::make_shared()); if (!cli->Init(connfd, peer)) cli.reset(); return cli; } break; #if QEDIS_CLUSTER case ConnectionTag::kSentinelClient: { DBG << "Connect success to cluster " << peer.ToString(); auto conn = std::make_shared(); if (!conn->Init(connfd, peer)) conn.reset(); return conn; } break; case ConnectionTag::kSlaveClient: { INF << "Connect to slave " << peer.ToString(); auto cli(std::make_shared()); if (!cli->Init(connfd, peer)) cli.reset(); return cli; } break; #endif case ConnectionTag::kMigrateClient: { INF << "Connect to migrate " << peer.ToString(); auto cli(std::make_shared()); if (!cli->Init(connfd, peer)) cli.reset(); return cli; } break; default: ERR << "Unknown tag " << tag; break; } return nullptr; } Time g_now; static void QdbCron() { using namespace qedis; if (g_qdbPid != -1) return; if (g_now.MilliSeconds() > (g_lastQDBSave + unsigned(g_config.saveseconds)) * 1000UL && QStore::dirty_ >= g_config.savechanges) { int ret = fork(); if (ret == 0) { { QDBSaver qdb; qdb.Save(g_config.rdbfullname.c_str()); std::cerr << "ServerCron child save rdb done, exiting child\n"; } // make qdb to be destructed before exit _exit(0); } else if (ret == -1) { ERR << "fork qdb save process failed"; } else { g_qdbPid = ret; } INF << "ServerCron save rdb file " << g_config.rdbfullname; } } static void LoadDbFromDisk() { using namespace qedis; // USE AOF RECOVERY FIRST, IF FAIL, THEN RDB QAOFLoader aofLoader; if (aofLoader.Load(g_config.appendfilename.c_str())) { const auto& cmds = aofLoader.GetCmds(); for (const auto& cmd : cmds) { const QCommandInfo* info = QCommandTable::GetCommandInfo(cmd[0]); QCommandTable::ExecuteCmd(cmd, info); } } else { QDBLoader loader; loader.Load(g_config.rdbfullname.c_str()); } } #if QEDIS_CLUSTER static void OnConnectClusterFail(const std::vector& addrs, size_t& i) { WRN << "Connect cluster failed " << addrs[i].ToString(); if (++i >= addrs.size()) i = 0; Timer* timer = TimerManager::Instance().CreateTimer(); timer->Init(2 * 1000, 1); timer->SetCallback([=, &i]() { USR << "OnTimer connect to " << addrs[i].GetIP() << ":" << addrs[i].GetPort(); Server::Instance()->TCPConnect(addrs[i], std::bind(OnConnectClusterFail, addrs, std::ref(i)), ConnectionTag::kSentinelClient); }); TimerManager::Instance().AsyncAddTimer(timer); }; #endif bool Qedis::_Init() { using namespace qedis; char runid[kRunidSize + 1] = ""; getRandomHexChars(runid, kRunidSize); g_config.runid.assign(runid, kRunidSize); if (port_ != 0) g_config.port = port_; if (!logLevel_.empty()) g_config.loglevel = logLevel_; if (!master_.empty()) { g_config.masterIp = master_; g_config.masterPort = masterPort_; } // process log { unsigned int level = ConvertLogLevel(g_config.loglevel), dest = 0; if (g_config.logdir == "stdout") dest = logConsole; else dest = logFILE; g_log = LogManager::Instance().CreateLog(level, dest, g_config.logdir.c_str()); } SocketAddr addr(g_config.ip.c_str(), g_config.port); if (!Server::TCPBind(addr, ConnectionTag::kQedisClient)) { ERR << "can not bind socket on port " << addr.GetPort(); return false; } QCommandTable::Init(); QCommandTable::AliasCommand(g_config.aliases); QSTORE.Init(g_config.databases); QSTORE.InitExpireTimer(); QSTORE.InitBlockedTimer(); QSTORE.InitEvictionTimer(); QSTORE.InitDumpBackends(); QPubsub::Instance().InitPubsubTimer(); QMigrationManager::Instance().InitMigrationTimer(); // Only if there is no backend, load aof or rdb if (g_config.backend == qedis::BackEndNone) LoadDbFromDisk(); QAOFThreadController::Instance().Start(); QSlowLog::Instance().SetThreshold(g_config.slowlogtime); QSlowLog::Instance().SetLogLimit(static_cast(g_config.slowlogmaxlen)); { auto cronTimer = TimerManager::Instance().CreateTimer(); cronTimer->Init(1000 / qedis::g_config.hz); cronTimer->SetCallback([]() { QdbCron(); }); TimerManager::Instance().AddTimer(cronTimer); } { auto repTimer = TimerManager::Instance().CreateTimer(); repTimer->Init(1500); repTimer->SetCallback([&]() { QREPL.Cron(); }); TimerManager::Instance().AddTimer(repTimer); } // master ip if (!g_config.masterIp.empty()) { QREPL.SetMasterAddr(g_config.masterIp.c_str(), g_config.masterPort); } // load so modules const auto& modules = g_config.modules; for (const auto& mod: modules) { try { MODULES.Load(mod.c_str()); std::cerr << "Load " << mod << " successful\n"; } catch (const ModuleNoLoad& e) { std::cerr << "Load " << mod << " failed\n"; } catch (const ModuleExist& e) { std::cerr << "Load " << mod << " failed because exist\n"; } catch (const std::runtime_error& e) { std::cerr << "Load " << mod << " failed because runtime error\n"; } catch (...) { std::cerr << "Load " << mod << " failed, unknown exception\n"; } } // output logo to console char logo[512] = ""; snprintf(logo, sizeof logo - 1, qedisLogo, QEDIS_VERSION, static_cast(sizeof(void*)) * 8, static_cast(g_config.port)); std::cerr << logo; #if QEDIS_CLUSTER // cluster if (g_config.enableCluster) { std::vector addrs; for (const auto& s : g_config.centers) { addrs.push_back(SocketAddr(s)); } std::function retry = std::bind(OnConnectClusterFail, addrs, std::ref(clusterIndex_)); Server::Instance()->TCPConnect(addrs[clusterIndex_], retry, ConnectionTag::kSentinelClient); } #endif return true; } static void CheckChild() { using namespace qedis; if (g_qdbPid == -1 && g_rewritePid == -1) return; int statloc = 0; pid_t pid = wait3(&statloc,WNOHANG,NULL); if (pid != 0 && pid != -1) { int exit = WEXITSTATUS(statloc); int signal = 0; if (WIFSIGNALED(statloc)) signal = WTERMSIG(statloc); if (pid == g_qdbPid) { QDBSaver::SaveDoneHandler(exit, signal); if (QREPL.IsBgsaving()) QREPL.OnRdbSaveDone(); else QREPL.TryBgsave(); } else if (pid == g_rewritePid) { INF << pid << " pid rewrite process success done."; QAOFThreadController::RewriteDoneHandler(exit, signal); } else { ERR << pid << " is not rdb or aof process "; assert (!!!"Is there any back process except rdb and aof?"); } } } bool Qedis::_RunLogic() { g_now.Now(); TimerManager::Instance().UpdateTimers(g_now); CheckChild(); return Server::_RunLogic(); } void Qedis::_Recycle() { std::cerr << "Qedis::_Recycle: server is exiting.. BYE BYE\n"; qedis::QAOFThreadController::Instance().Stop(); } int main(int ac, char* av[]) { Qedis svr; if (!svr.ParseArgs(ac - 1, av + 1)) { Usage(); return -1; } if (!svr.GetConfigName().empty()) { if (!LoadQedisConfig(svr.GetConfigName().c_str(), qedis::g_config)) { std::cerr << "Load config file [" << svr.GetConfigName() << "] failed!\n"; return -2; } } svr.MainLoop(qedis::g_config.daemonize); return 0; } ================================================ FILE: QedisSvr/Qedis.h ================================================ // // qedis.h // // Created by Bert Young on 16-1-22. // Copyright (c) 2016年 Bert Young. All rights reserved. // #include "QString.h" #include "Server.h" #define QEDIS_VERSION "1.0.0" class Qedis : public Server { public: Qedis(); ~Qedis(); bool ParseArgs(int ac, char* av[]); const qedis::QString& GetConfigName() const { return cfgFile_; } private: std::shared_ptr _OnNewConnection(int fd, int tag) override; bool _Init() override; bool _RunLogic() override; void _Recycle() override; qedis::QString cfgFile_; unsigned short port_; qedis::QString logLevel_; qedis::QString master_; unsigned short masterPort_; #if QEDIS_CLUSTER // cluster size_t clusterIndex_ = 0; #endif static const unsigned kRunidSize; }; ================================================ FILE: QedisSvr/QedisLogo.h ================================================ #ifndef BERT_QEDISLOGO_H #define BERT_QEDISLOGO_H const char* qedisLogo = "\n _____ _____ _____ _ _____ \n" " / _ \\ | ____| | _ \\ | | / ___/\n" " | | | | | |__ | | | | | | | |___ Qedis(%s) %d bits, another redis written in C++11\n" // version and server bits " | | | | | __| | | | | | | \\___ \\ Port: %d\n" " | |_| |_ | |___ | |_| | | | ___| | Author: Bert Young\n" " \\_______| |_____| |_____/ |_| /_____/ https://github.com/loveyacper/Qedis\n\n\n"; #endif ================================================ FILE: README.md ================================================ _____ _____ _____ _ ______ / _ \ | ____| | _ \ | | / ___/ | | | | | |__ | | | | | | | |____ | | | | | __| | | | | | | \__ \ | |_| |_ | |___ | |_| | | | ___| | \_______| |_____| |_____/ |_| /_____/ [![Build Status](https://travis-ci.org/loveyacper/Qedis.svg?branch=master)](https://travis-ci.org/loveyacper/Qedis) [看中文说明请点我](README.zh.md) A C++11 implementation of distributed redis server, use Leveldb for persist storage.(including cluster) ## Requirements * C++11 & CMake * Linux or OS X ## Cluster Features Use zookeeper for leader election, to reach high availability. Of course you can also use redis-sentinel. See details in [cluster Readme](QCluster/README.md), still in development. ## Fully compatible with redis 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. In a word, Qedis is full compatible with Redis. ## Support module for write your own extensions Qedis supports module now, still in progress, much work to do. I added three commands(ldel, skeys, hgets) for demonstration. ## Persistence: Not limited to memory Leveldb can be configured as backend for Qedis. ## High Performance - Qedis is approximately 20-25% faster than redis if run benchmark with pipeline requests(set -P = 50 or higher). - Average 80K requests per seconds for write, and 90K requests per seconds for read. - Before run test, please ensure that std::list::size() is O(1), obey the C++11 standards. Run this command, compare with redis use pipeline commands, try it. ```bash ./redis-benchmark -q -n 1000000 -P 50 -c 50 ``` ![image](https://github.com/loveyacper/Qedis/blob/master/performance.png) ## Support LRU cache When memory is low, you can make Qedis to free memory by evict some key according to LRU. ## Master-slave Replication, transaction, RDB/AOF, slow log, publish-subscribe Qedis supports them all :-) ## Command List #### show all supported commands list, about 140 commands - cmdlist ## TODO * Support lua * Golang Cluster client ================================================ FILE: README.zh.md ================================================ _____ _____ _____ _ ______ / _ \ | ____| | _ \ | | / ___/ | | | | | |__ | | | | | | | |____ | | | | | __| | | | | | | \__ \ | |_| |_ | |___ | |_| | | | ___| | \_______| |_____| |_____/ |_| /_____/ [![Build Status](https://travis-ci.org/loveyacper/Qedis.svg?branch=master)](https://travis-ci.org/loveyacper/Qedis) [Click me switch to English](README.md) C++11实现的增强版分布式Redis服务器,使用Leveldb作为持久化存储引擎。 ## 环境需求 * C++11、CMake * Linux 或 MAC OS ## 集群特性 可以搭建Zookeeper,监视一组互为主备的Qedis进程以实现高可用; 当然也可以使用官方redis-sentinel。 详见[cluster Readme](QCluster/README.md) scale-out集群正在开发中... ## 与Redis完全兼容 你可以用redis的各种工具来测试Qedis,比如官方的redis-cli, redis-benchmark。 Qedis可以和redis之间进行复制,可以读取redis的rdb文件或aof文件。当然,Qedis生成的aof或rdb文件也可以被redis读取。 你还可以用redis-sentinel来实现Qedis的高可用! 总之,Qedis与Redis完全兼容。 ## 高性能 - Qedis性能大约比Redis3.2高出20%(使用redis-benchmark测试pipeline请求,比如设置-P=50或更高) - Qedis的高性能有一部分得益于独立的网络线程处理IO,因此和redis比占了便宜。但Qedis逻辑仍然是单线程的。 - 另一部分得益于C++ STL的高效率(CLANG的表现比GCC更好)。 - 在测试前,你要确保std::list的size()是O(1)复杂度,这才遵循C++11的标准。否则list相关命令不可测。 运行下面这个命令,试试和redis比一比~ ```bash ./redis-benchmark -q -n 1000000 -P 50 -c 50 ``` 我在rMBP late2013笔记本上测试结果如图: ![image](https://github.com/loveyacper/Qedis/blob/master/performance.png) ## 编写扩展模块 Qedis支持动态库模块,可以在运行时添加新命令。 我添加了三个命令(ldel, skeys, hgets)作为演示。 ## 支持冷数据淘汰 是的,在内存受限的情况下,你可以让Qedis根据简单的LRU算法淘汰一些key以释放内存。 ## 主从复制,事务,RDB/AOF持久化,慢日志,发布订阅 这些特性Qedis都有:-) ## 持久化:内存不再是上限 Leveldb可以配置为Qedis的持久化存储引擎,可以存储更多的数据。 ## 命令列表 #### 展示Qedis支持的所有命令,目前支持140个命令 - cmdlist ## TODO * 支持lua * Qedis Cluster多语言客户端 ================================================ FILE: UnitTest/CMakeLists.txt ================================================ INCLUDE(${PROJECT_SOURCE_DIR}/CMakeCommon) AUX_SOURCE_DIRECTORY(. UNITTEST_SRC) LINK_DIRECTORIES(../../leveldb) LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QedisCore) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/QBase) ADD_EXECUTABLE(qunittest ${UNITTEST_SRC}) SET(EXECUTABLE_OUTPUT_PATH ../../bin) TARGET_LINK_LIBRARIES(qunittest qediscore; leveldb) ADD_DEPENDENCIES(qunittest qediscore) ================================================ FILE: UnitTest/QGlobRegex_unittest.cc ================================================ #include "UnitTest.h" #include "QGlobRegex.h" using namespace qedis; TEST_CASE(regex_star) { EXPECT_TRUE(glob_match("a*b?*?", "abxy")); EXPECT_TRUE(glob_match("*a", "aa")); EXPECT_TRUE(glob_match("*a", "abaa")); EXPECT_TRUE(glob_match("***a", "aaaa")); EXPECT_FALSE(glob_match("a*b?*?", "cb")); EXPECT_TRUE(glob_match("a*b", "ab")); EXPECT_TRUE(glob_match("a*b", "acb")); EXPECT_TRUE(glob_match("a*b", "acccb")); EXPECT_FALSE(glob_match("a*b", "accc")); } TEST_CASE(regex_question) { EXPECT_FALSE(glob_match("a?b", "ab")); EXPECT_TRUE(glob_match("a?b", "acb")); EXPECT_FALSE(glob_match("a?b", "acc")); EXPECT_FALSE(glob_match("a?b", "accb")); } TEST_CASE(regex_bracket) { EXPECT_TRUE(glob_match("[-]", "-")); EXPECT_TRUE(glob_match("[-abc]", "-")); EXPECT_TRUE(glob_match("[-]??", "--x")); EXPECT_TRUE(glob_match("[a-ce-a]", "a")); EXPECT_TRUE(glob_match("[a-eg-z]", "u")); EXPECT_FALSE(glob_match("[^a-eg-z]", "u")); EXPECT_TRUE(glob_match("[abcx-z]", "c")); EXPECT_TRUE(glob_match("[abcx-z]", "y")); EXPECT_FALSE(glob_match("[^abcx-z]", "c")); EXPECT_FALSE(glob_match("[^abcx-z]", "y")); EXPECT_FALSE(glob_match("[a-z]", "U")); EXPECT_TRUE(glob_match("[^a-z]", "U")); EXPECT_TRUE(glob_match("[a--]", "-")); EXPECT_FALSE(glob_match("[^a--]", "-")); EXPECT_TRUE(glob_match("[---]", "-")); EXPECT_TRUE(glob_match("[-]", "-")); EXPECT_TRUE(glob_match("[-xyz]", "-")); EXPECT_TRUE(glob_match("[]]", "]")); EXPECT_TRUE(glob_match("x[]abc]", "x]")); EXPECT_FALSE(glob_match("x[ab]c]", "x]")); EXPECT_TRUE(glob_match("x\\[ab]y", "x[ab]y")); EXPECT_TRUE(glob_match("x*\\", "xab\\")); EXPECT_TRUE(glob_match("\\*\\", "\\fuckyou\\")); } TEST_CASE(regex_star_brackets) { EXPECT_FALSE(glob_match("[a-e]", "xa")); EXPECT_FALSE(glob_match("*[a-e]*", "x")); EXPECT_FALSE(glob_match("*[a-e]*", "xy")); EXPECT_TRUE(glob_match("*[a-e]*", "xya")); EXPECT_TRUE(glob_match("*[a-e]*", "xay")); EXPECT_TRUE(glob_match("*[a-e]*", "axy")); } TEST_CASE(regex_search) { EXPECT_TRUE(glob_search("[a-e]", "xa")); EXPECT_FALSE(glob_search("[a-e]", "x")); EXPECT_FALSE(glob_search("[a-e]", "xy")); EXPECT_TRUE(glob_search("[a-e]", "xya")); EXPECT_TRUE(glob_search("[a-e]", "xay")); EXPECT_TRUE(glob_search("[a-e]", "axy")); } TEST_CASE(regex_strange) { EXPECT_FALSE(glob_match("[a-e", "a")); EXPECT_TRUE(glob_match("*a-e]*", "a-e]")); EXPECT_FALSE(glob_match("[a-e*", "a")); EXPECT_TRUE(glob_match("a[-]", "a-")); EXPECT_FALSE(glob_match("a\[", "a[")); EXPECT_TRUE(glob_match("a\\[", "a[")); EXPECT_TRUE(glob_match("a[]]", "a]")); EXPECT_TRUE(glob_match("a[^^]", "a$")); EXPECT_FALSE(glob_match("a[^^]", "a^")); EXPECT_TRUE(glob_match("a**[^^]", "a^!")); EXPECT_FALSE(glob_match("**a**", "xyz")); } ================================================ FILE: UnitTest/UnitTest.cc ================================================ #include "UnitTest.h" #include enum Color { Color_red = 1, Color_green , Color_yellow , Color_normal , Color_blue , Color_purple , Color_white , Color_max , }; void SetColor(Color c) { static const char* colors[Color_max] = { "", "\033[1;31;40m", "\033[1;32;40m", "\033[1;33;40m", "\033[0m", "\033[1;34;40m", "\033[1;35;40m", "\033[1;37;40m", }; fprintf(stdout, "%s", colors[c]); } UnitTestBase::UnitTestBase() : pass_(true), abort_(false) { UnitTestManager::Instance().AddTest(this); } UnitTestBase& UnitTestBase::SetInfo(const std::string& exprInfo, bool pass, bool abort) { pass_ = pass; abort_ = abort; expr_ = (pass_ ? "[passed]: " : "[failed]: ") + exprInfo; return *this; } void UnitTestBase::Print() const { SetColor(Color_red); for (const auto& e : errors_) std::cout << e << std::endl; SetColor(Color_normal); } void UnitTestBase::FlushError() { if (pass_) { SetColor(Color_green); std::cout << expr_ << std::endl; return; } errors_.push_back(expr_); if (abort_) { Print(); ::abort(); } } // test mgr UnitTestManager& UnitTestManager::Instance() { static UnitTestManager mgr; return mgr; } void UnitTestManager::AddTest(UnitTestBase* test) { tests_.push_back(test); } void UnitTestManager::Clear() { tests_.clear(); } void UnitTestManager::Run() { std::size_t pass = 0; std::size_t fail = 0; for (const auto& ut : tests_) { ut->Run(); if (ut->IsFine()) { ++ pass; SetColor(Color_white); std::cout << "ALL PASSED! " << ut->GetName() << std::endl; } else { ++ fail; ut->Print(); SetColor(Color_purple); std::cout << "FAILED! " << ut->GetName() << std::endl; } } Clear(); SetColor(fail == 0 ? Color_blue: Color_yellow); std::cout << (pass + fail) << " cases: " << pass << " passed, " << fail << " failed\n"; SetColor(Color_normal); } int main() { RUN_ALL_TESTS(); return 0; } ================================================ FILE: UnitTest/UnitTest.h ================================================ #ifndef BERT_UNITTEST_H #define BERT_UNITTEST_H #include #include #include class UnitTestBase { public: friend class MsgHelper; UnitTestBase(); // stack only, no need virtual destructor, but the warning... virtual ~UnitTestBase() {} const std::string& GetName() const { return name_; } virtual void Run() = 0; bool IsFine() const { return errors_.empty(); } void Print() const; template UnitTestBase& operator << (const T & t); protected: UnitTestBase& SetInfo(const std::string& exprInfo, bool pass = true, bool abort = false); std::string name_; private: void FlushError(); bool pass_; bool abort_; std::string expr_; std::vector errors_; private: UnitTestBase(const UnitTestBase& ) = delete; UnitTestBase& operator= (const UnitTestBase& ) = delete; void* operator new(std::size_t ); // stack only }; template inline UnitTestBase& UnitTestBase::operator<< (const T & t) { if (!pass_) { std::ostringstream str; str << t; expr_ += str.str(); } return *this; } class MsgHelper { public: void operator=(UnitTestBase& test) { test.FlushError(); } }; #define TEST_CASE(name) \ class UnitTestBase##name: public UnitTestBase \ { \ public: \ UnitTestBase##name() { \ name_ = #name; \ } \ virtual void Run(); \ } test_##name##_obj; \ void UnitTestBase##name::Run() #define EXPECT_TRUE(expr) \ MsgHelper()=((expr) ? SetInfo("'"#expr"'", true) : SetInfo("'"#expr"'", false)) #define EXPECT_FALSE(expr) \ EXPECT_TRUE(!(expr)) #define ASSERT_TRUE(expr) \ MsgHelper()=((expr) ? SetInfo("'"#expr"'", true) : SetInfo("'"#expr"' ", false, true)) #define ASSERT_FALSE(expr) \ ASSERT_TRUE(!(expr)) class UnitTestManager { public: static UnitTestManager& Instance(); void AddTest(UnitTestBase* test); void Clear(); void Run(); private: UnitTestManager() {} std::vector tests_; }; #define RUN_ALL_TESTS UnitTestManager::Instance().Run #endif ================================================ FILE: cluster/.gitignore ================================================ # MAC OS .DS_Store # build/ bin/ ================================================ FILE: cluster/CMakeCommon ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++0x -lpthread") SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wall -std=c++0x -lpthread") ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions") SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -g -Wall -std=c++1y -stdlib=libc++ -Wc++11-extensions") ADD_DEFINITIONS(-Dthread_local=__thread) ELSE() message(FATAL_ERROR "Only support linux or OS X") ENDIF() OPTION(DEBUG "Debug or release" ON) IF(DEBUG) SET(CMAKE_BUILD_TYPE "Debug") ELSE() SET(CMAKE_BUILD_TYPE "Release") ENDIF() ================================================ FILE: cluster/CMakeLists.txt ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(QEDIS_CLUSTER) SUBDIRS(ananas) SET(USE_ZOOKEEPER 1) ADD_DEFINITIONS(-DUSE_ZOOKEEPER=${USE_ZOOKEEPER}) SUBDIRS(cluster_conn) SUBDIRS(qedis_proxy) ================================================ FILE: cluster/Makefile ================================================ all: mkdir -p build && cd build && cmake .. && make clean: cd build && make clean ================================================ FILE: cluster/README.md ================================================ # Qedis分布式集群 ## 环境需求 * C++11、CMake * zookeeper * Linux 或 MAC OS ## 代码目录 * ananas 一个C++11编写的网络库,提供了强大的future异步编程模式. * cluster_conn 针对zookeeper或etcd的包装。目前只提供了zookeeper. * qedis_proxy Qedis代理服务器,负责发现Qedis服务,转发客户端请求和Qedis服务器响应. ## Future模式 本目录代码采用了基于Future模式的异步编程,例如与zookeeper集群连接时,需要进行7个步骤: * 握手 * 注册自己,同时获取Qedis set信息,这是两个并行的异步请求 * 根据set信息,获取分片信息 * 存储分片信息 * 发起异步请求:获取Qedis服务列表 * 存储Qedis服务信息 * 初始化ping定时器 * 如若以上操作任一无响应,触发超时逻辑 这一连串的操作使用future模式编写如下,每一个Then都是前面异步请求的回调: ```cpp ctx_->DoHandshake() .Then([me = this](const ZkResponse& rsp) mutable { return me->_ProcessHandshake(rsp); }) .Then([me = this](ananas::Try&& tctx) mutable { return me->_RegisterAndGetServers(std::move(tctx)); }) .Then([me = this](const std::vector >& rsps) mutable { return me->_GetShardingInfo(rsps); }) .Then([me = this](const std::vector >& vrsp) mutable { if (!me->_ProcessShardingInfo(vrsp)) { using InnerType = std::vector>; auto exp = std::runtime_error("ProcessShardingInfo failed"); return ananas::MakeExceptionFuture(exp); } // 5. get qedis server's list and watch the qedis server list return me->_GetServers(vrsp); }) .Then([me = this](const std::vector >& vrsp) mutable { return me->_ProcessServerInfo(vrsp); }) .Then([me = this](bool succ) { if (succ) me->_InitPingTimer(); }) .OnTimeout(std::chrono::seconds(3), []() { // 3秒钟超时 std::cout << "OnTimeout handshake\n"; ananas::EventLoop::ExitApplication(); }, conn_->GetLoop() ); ``` ## 集群特性 待写,代码实现中。。。 ================================================ FILE: cluster/ananas/CMakeLists.txt ================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) SUBDIRS(net) ================================================ FILE: cluster/ananas/future/Future.h ================================================ #ifndef BERT_FUTURE_H #define BERT_FUTURE_H #include #include #include #include #include "Helper.h" #include "Try.h" #include "util/Scheduler.h" namespace ananas { namespace internal { enum class Progress { None, Timeout, Done }; using TimeoutCallback = std::function; template struct State { static_assert(std::is_same::value || std::is_copy_constructible(), "must be copyable or void"); static_assert(std::is_same::value || std::is_move_constructible(), "must be movable or void"); State() : progress_(Progress::None), retrieved_ {false} { } std::mutex thenLock_; Try value_; std::function&& )> then_; Progress progress_; std::function onTimeout_; std::atomic retrieved_; bool IsRoot() const { return !onTimeout_; } }; } // end namespace internal template class Future; template class Promise { public: Promise() : state_(std::make_shared>()) { } // TODO: C++11 lambda doesn't support move capture // just for compile, copy Promise is undefined, do NOT do that! Promise(const Promise&) = default; Promise& operator= (const Promise&) = default; Promise(Promise&& pm) = default; Promise& operator= (Promise&& pm) = default; void SetException(std::exception_ptr exp) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = Try(std::move(exp)); if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(SHIT&& t) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = std::move(t); if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(const SHIT& t) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = t; if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(Try&& t) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = std::move(t); if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(const Try& t) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = t; if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(Try&& ) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = Try(); if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue(const Try& ) { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = Try(); if (state_->then_) state_->then_(std::move(state_->value_)); } template typename std::enable_if::value, void>::type SetValue() { std::unique_lock guard(state_->thenLock_); bool isRoot = state_->IsRoot(); if (isRoot && state_->progress_ != internal::Progress::None) return; state_->progress_ = internal::Progress::Done; state_->value_ = Try(); if (state_->then_) state_->then_(std::move(state_->value_)); } Future GetFuture() { bool expect = false; if (!state_->retrieved_.compare_exchange_strong(expect, true)) { struct FutureAlreadyRetrieved {}; throw FutureAlreadyRetrieved(); } return Future(state_); } private: std::shared_ptr> state_; }; template class Future { public: using InnerType = T; template friend class Future; Future() { } Future(const Future&) = delete; void operator= (const Future&) = delete; Future(Future&& fut) = default; Future& operator= (Future&& fut) = default; explicit Future(std::shared_ptr> state) : state_(std::move(state)) { } template > auto Then(F&& f) -> typename R::ReturnFutureType { typedef typename R::Arg Arguments; return _ThenImpl(nullptr, std::forward(f), Arguments()); } // f will be called in sched template > auto Then(Scheduler* sched, F&& f) -> typename R::ReturnFutureType { typedef typename R::Arg Arguments; return _ThenImpl(sched, std::forward(f), Arguments()); } //1. F does not return future type template typename std::enable_if::type _ThenImpl(Scheduler* sched, F&& f, internal::ResultOfWrapper ) { static_assert(std::is_void::value ? sizeof...(Args) == 0 : sizeof...(Args) == 1, "Then callback must take 0/1 argument"); using FReturnType = typename R::IsReturnsFuture::Inner; using namespace internal; Promise pm; auto nextFuture = pm.GetFuture(); std::unique_lock guard(state_->thenLock_); if (state_->progress_ == Progress::Timeout) { struct FutureWrongState {}; throw FutureWrongState(); } else if (state_->progress_ == Progress::Done) { Try t; try { t = std::move(state_->value_); } catch(const std::exception& e) { t = Try(std::current_exception()); } guard.unlock(); auto func = [res = std::move(t), f = std::move((typename std::decay::type)f), prom = std::move(pm)]() mutable { auto result = WrapWithTry(f, std::move(res)); prom.SetValue(std::move(result)); }; if (sched) sched->ScheduleOnce(std::move(func)); else func(); } else { // 1. set pm's timeout callback nextFuture.SetOnTimeout([weak_parent = std::weak_ptr>(state_)](TimeoutCallback&& cb) { auto parent = weak_parent.lock(); if (!parent) return; { std::unique_lock guard(parent->thenLock_); if (parent->progress_ != Progress::None) return; parent->progress_ = Progress::Timeout; } if (!parent->IsRoot()) parent->onTimeout_(std::move(cb)); // propogate to the root else cb(); }); // 2. set this future's then callback SetCallback([sched, func = std::move((typename std::decay::type)f), prom = std::move(pm)](Try&& t) mutable { auto cb = [func = std::move(func), t = std::move(t), prom = std::move(prom)]() mutable { // run callback, T can be void, thanks to folly Try<> auto result = WrapWithTry(func, std::move(t)); // set next future's result prom.SetValue(std::move(result)); }; if (sched) sched->ScheduleOnce(std::move(cb)); else cb(); }); } return std::move(nextFuture); } //2. F return another future type template typename std::enable_if::type _ThenImpl(Scheduler* sched, F&& f, internal::ResultOfWrapper) { static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); using FReturnType = typename R::IsReturnsFuture::Inner; using namespace internal; Promise pm; auto nextFuture = pm.GetFuture(); std::unique_lock guard(state_->thenLock_); if (state_->progress_ == Progress::Timeout) { struct FutureWrongState {}; throw FutureWrongState(); } else if (state_->progress_ == Progress::Done) { Try t; try { t = std::move(state_->value_); } catch(const std::exception& e) { t = Try(std::current_exception()); } guard.unlock(); auto cb = [res = std::move(t), f = std::move((typename std::decay::type)f), prom = std::move(pm)]() mutable { // because func return another future: innerFuture, when innerFuture is done, nextFuture can be done auto innerFuture = f(res.template Get()...); std::unique_lock guard(innerFuture.state_->thenLock_); if (innerFuture.state_->progress_ == Progress::Timeout) { struct FutureWrongState {}; throw FutureWrongState(); } else if (innerFuture.state_->progress_ == Progress::Done) { Try t; try { t = std::move(innerFuture.state_->value_); } catch(const std::exception& e) { t = Try(std::current_exception()); } guard.unlock(); prom.SetValue(std::move(t)); } else { innerFuture.SetCallback([prom = std::move(prom)](Try&& t) mutable { prom.SetValue(std::move(t)); }); } }; if (sched) sched->ScheduleOnce(std::move(cb)); else cb(); } else { // 1. set pm's timeout callback nextFuture.SetOnTimeout([weak_parent = std::weak_ptr>(state_)](TimeoutCallback&& cb) { auto parent = weak_parent.lock(); if (!parent) return; { std::unique_lock guard(parent->thenLock_); if (parent->progress_ != Progress::None) return; parent->progress_ = Progress::Timeout; } if (!parent->IsRoot()) parent->onTimeout_(std::move(cb)); // propogate to the root else cb(); }); // 2. set this future's then callback SetCallback([sched = sched, func = std::move((typename std::decay::type)f), prom = std::move(pm)](Try&& t) mutable { auto cb = [func = std::move(func), t = std::move(t), prom = std::move(prom)]() mutable { // because func return another future: innerFuture, when innerFuture is done, nextFuture can be done auto innerFuture = func(t.template Get()...); std::unique_lock guard(innerFuture.state_->thenLock_); if (innerFuture.state_->progress_ == Progress::Timeout) { struct FutureWrongState {}; throw FutureWrongState(); } else if (innerFuture.state_->progress_ == Progress::Done) { Try t; try { t = std::move(innerFuture.state_->value_); } catch(const std::exception& e) { t = Try(std::current_exception()); } guard.unlock(); prom.SetValue(std::move(t)); } else { innerFuture.SetCallback([prom = std::move(prom)](Try&& t) mutable { prom.SetValue(std::move(t)); }); } }; if (sched) sched->ScheduleOnce(std::move(cb)); else cb(); }); } return std::move(nextFuture); } void SetCallback(std::function&& )>&& func) { state_->then_ = std::move(func); } void SetOnTimeout(std::function&& func) { state_->onTimeout_ = std::move(func); } /* * When register callbacks and timeout for a future like this: * * Future f; * f.Then(xx).Then(yy).OnTimeout(zz); * * There will be 3 future objects created except f, we call f as root future. * The zz callback is registed on the last future, however, timeout and future satisfication * can happened almost in the same time, we should ensure that both xx and yy will be called * or zz will be called, but they can't happened both or neither. So we pass the cb * to the root future, if we find out that root future is indeed timeout, we call cb there. */ void OnTimeout(std::chrono::milliseconds duration, internal::TimeoutCallback f, Scheduler* scheduler) { scheduler->ScheduleOnceAfter(duration, [state = state_, cb = std::move(f)]() mutable { { std::unique_lock guard(state->thenLock_); if (state->progress_ != internal::Progress::None) return; state->progress_ = internal::Progress::Timeout; } if (!state->IsRoot()) state->onTimeout_(std::move(cb)); // propogate to the root future else cb(); }); } private: std::shared_ptr> state_; }; // Make ready future template inline Future MakeReadyFuture(T2&& value) { Promise pm; auto f(pm.GetFuture()); pm.SetValue(std::forward(value)); return f; } template inline Future MakeReadyFuture(const T2& value) { Promise pm; auto f(pm.GetFuture()); pm.SetValue(value); return f; } inline Future MakeReadyFuture() { Promise pm; auto f(pm.GetFuture()); pm.SetValue(); return f; } // Make exception future template inline Future MakeExceptionFuture(const std::exception& exp) { Promise pm; auto f(pm.GetFuture()); pm.SetException(std::make_exception_ptr(exp)); return f; } // When All template typename internal::CollectAllVariadicContext::type::InnerType...>::FutureType WhenAll(FT&&... futures) { auto ctx = std::make_shared::type::InnerType...>>(); internal::CollectVariadicHelper( ctx, std::forward::type>(futures)...); return ctx->pm.GetFuture(); } template Future< std::vector< Try::value_type::InnerType>>> WhenAll(InputIterator first, InputIterator last) { using T = typename std::iterator_traits::value_type::InnerType; if (first == last) return MakeReadyFuture(std::vector>()); struct CollectAllContext { CollectAllContext(int n) : results(n) {} ~CollectAllContext() { // I think this line is useless. // pm.SetValue(std::move(results)); } Promise>> pm; std::vector> results; std::atomic collected{0}; }; auto ctx = std::make_shared(std::distance(first, last)); for (size_t i = 0; first != last; ++first, ++i) { first->SetCallback([ctx, i](Try&& t) { ctx->results[i] = std::move(t); if (ctx->results.size() - 1 == std::atomic_fetch_add (&ctx->collected, std::size_t(1))) { ctx->pm.SetValue(std::move(ctx->results)); } }); } return ctx->pm.GetFuture(); } // When Any template Future< std::pair::value_type::InnerType>>> WhenAny(InputIterator first, InputIterator last) { using T = typename std::iterator_traits::value_type::InnerType; if (first == last) { return MakeReadyFuture(std::make_pair(size_t(0), Try(T()))); } struct CollectAnyContext { CollectAnyContext() {}; Promise>> pm; std::atomic done{false}; }; auto ctx = std::make_shared(); for (size_t i = 0; first != last; ++first, ++i) { first->SetCallback([ctx, i](Try&& t) { if (!ctx->done.exchange(true)) { ctx->pm.SetValue(std::make_pair(i, std::move(t))); } }); } return ctx->pm.GetFuture(); } // When N template Future< std::vector< std::pair::value_type::InnerType>> > > WhenN(size_t N, InputIterator first, InputIterator last) { using T = typename std::iterator_traits::value_type::InnerType; size_t nFutures = std::distance(first, last); const size_t needCollect = std::min(nFutures, N); if (needCollect == 0) { return MakeReadyFuture(std::vector>>()); } struct CollectNContext { CollectNContext(size_t _needs) : needs(_needs) {} Promise>>> pm; std::mutex mutex; std::vector>> results; const size_t needs; bool done {false}; }; auto ctx = std::make_shared(needCollect); for (size_t i = 0; first != last; ++first, ++i) { first->SetCallback([ctx, i](Try&& t) { std::unique_lock guard(ctx->mutex); if (ctx->done) return; ctx->results.push_back(std::make_pair(i, std::move(t))); if (ctx->needs == ctx->results.size()) { ctx->done = true; ctx->pm.SetValue(std::move(ctx->results)); } }); } return ctx->pm.GetFuture(); } } // end namespace ananas #endif ================================================ FILE: cluster/ananas/future/Helper.h ================================================ #ifndef BERT_HELPER_H #define BERT_HELPER_H /* * This file is modified from facebook folly, with my annotation */ /* * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include namespace ananas { template class Future; template class Promise; template class Try; namespace internal { template using ResultOf = decltype(std::declval()(std::declval()...)); // I don't know why, but must do it to cater compiler... template struct ResultOfWrapper { using Type = ResultOf; }; // Test if F can be called with Args type template struct CanCallWith { // SFINAE Check template> static constexpr std::true_type Check(std::nullptr_t dummy) { return std::true_type{}; }; template static constexpr std::false_type Check(...) { return std::false_type{}; }; typedef decltype(Check(nullptr)) type; // true_type if T可以接受Args作为参数 static constexpr bool value = type::value; // the integral_constant's value }; // simple traits template struct IsFuture : std::false_type { using Inner = T; }; template struct IsFuture> : std::true_type { typedef T Inner; }; template struct CallableResult { // Test F call with arg type: void, T&&, T&, but do Not choose Try type as args typedef typename std::conditional< CanCallWith::value, // if true, F can call with void ResultOfWrapper, typename std::conditional< // NO, F(void) is invalid CanCallWith::value, // if true, F(T&&) is valid ResultOfWrapper, // Yes, F(T&&) is ok ResultOfWrapper >::type>::type Arg; // Resort to F(T&) // If ReturnsFuture::value is true, F returns another future type. typedef IsFuture IsReturnsFuture; // Future callback's result must be wrapped in another future typedef Future ReturnFutureType; }; // CallableResult specilization for void. // I don't know why folly works without this... template struct CallableResult { // Test F call with arg type: void typedef ResultOfWrapper Arg; // If ReturnsFuture::value is true, F returns another future type. typedef IsFuture IsReturnsFuture; // Future callback's result must be wrapped in another future typedef Future ReturnFutureType; }; // For when_all // template struct CollectAllVariadicContext { CollectAllVariadicContext() {} // Differ from folly: Do nothing here ~CollectAllVariadicContext() { } CollectAllVariadicContext(const CollectAllVariadicContext& ) = delete; void operator= (const CollectAllVariadicContext& ) = delete; template inline void SetPartialResult(Try& t) { std::get(results) = std::move(t); collects.push_back(I); if (collects.size() == std::tuple_size::value) pm.SetValue(std::move(results)); } Promise...>> pm; std::tuple...> results; std::vector collects; typedef Future...>> FutureType; }; // base template template