[
  {
    "path": ".clang-tidy",
    "content": "---\n# Only run a few checks for now.\nChecks:          'performance-*'\nWarningsAsErrors: ''\nHeaderFilterRegex: ''\nAnalyzeTemporaryDtors: false\nFormatStyle:     none\nUser:            chenshuo\nCheckOptions:\n  - key:             google-runtime-references.WhiteListTypes\n    value:           'muduo::MutexLock'\n  - key:             llvm-namespace-comment.ShortNamespaceLines\n    value:           '10'\n  - key:             llvm-namespace-comment.SpacesBeforeComments\n    value:           '2'\n  - key:   MuduoRootDirectory\n    value: '/muduo-cpp11/'\nExtraArgs:\n  - '-Wno-sign-conversion'\n...\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nDELETE FROM HERE TO LINE 18 BEFORE OPEN AN ISSUE.\n\nGitHub issues are for tracking bugs, not for general discussing like a forum.\n\nIf you have a general question to ask, use GitHub discussions:\n\nhttps://github.com/chenshuo/muduo/discussions\n\nWhen filing an issue of muduo, please provide a [SSCCE](http://sscce.org):\nShort, Self Contained, Correct (Compilable), Example.\n\nIf you can't compile muduo, make sure you install `cmake` and `boost` from the\nofficial package repository, e.g. `apt` or `yum`, before opening a bug.\nDon't open a bug if you installed boost from a third-party source or\ndownloaded it by yourself, and couldn't compile muduo, thank you.\n\nAlso specify the exact environment where the issue occurs:\n\n## Linux distro and version? x86 or ARM? 32-bit or 64-bit?\n\n## Branch (cpp98/cpp11/cpp17) and version of muduo?\n\n## Version of cmake, gcc and boost? (If not from distro.)\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "DELETE FROM HERE TO LINE 18 BEFORE OPEN AN ISSUE.\n\nGitHub issues are for tracking bugs, not for general discussing like a forum.\n\nIf you have a general question to ask, use GitHub discussions:\n\nhttps://github.com/chenshuo/muduo/discussions\n\nWhen filing an issue of muduo, please provide a [SSCCE](http://sscce.org):\nShort, Self Contained, Correct (Compilable), Example.\n\nIf you can't compile muduo, make sure you install `cmake` and `boost` from the\nofficial package repository, e.g. `apt` or `yum`, before opening a bug.\nDon't open a bug if you installed boost from a third-party source or\ndownloaded it by yourself, and couldn't compile muduo, thank you.\n\nAlso specify the exact environment where the issue occurs:\n\n## Linux distro and version? x86 or ARM? 32-bit or 64-bit?\n\n## Branch (cpp98/cpp11/cpp17) and version of muduo?\n\n## Version of cmake, gcc and boost? (If not from distro.)\n\n"
  },
  {
    "path": ".gitignore",
    "content": "*.swp\nbazel-*\ncompile_commands.json\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: cpp\nsudo: required\ncompiler:\n  - gcc\n  - clang\nos:\n  - linux\ninstall:\n  - sudo apt-get install libboost-dev\n  - sudo apt-get install libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev\n  - sudo apt-get install libboost-test-dev libboost-program-options-dev libboost-system-dev\n  - sudo apt-get install libc-ares-dev libcurl4-openssl-dev\n  - sudo apt-get install zlib1g-dev libgd-dev\nenv:\n  - BUILD_TYPE=debug\n  - BUILD_TYPE=release\nscript:\n  - ./build.sh\n"
  },
  {
    "path": "BUILD.bazel",
    "content": "# See https://github.com/chenshuo/muduo-tutorial for how to use muduo in your project.\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.6)\n\nproject(muduo C CXX)\n\nenable_testing()\n\nif(NOT CMAKE_BUILD_TYPE)\n  set(CMAKE_BUILD_TYPE \"Release\")\nendif()\n\n# only build examples if this is the main project\nif(CMAKE_PROJECT_NAME STREQUAL \"muduo\")\n  option(MUDUO_BUILD_EXAMPLES \"Build Muduo examples\" ON)\nendif()\n\nset(CXX_FLAGS\n -g\n # -DVALGRIND\n -DCHECK_PTHREAD_RETURN_VALUE\n -D_FILE_OFFSET_BITS=64\n -Wall\n -Wextra\n -Werror\n -Wconversion\n -Wno-unused-parameter\n -Wold-style-cast\n -Woverloaded-virtual\n -Wpointer-arith\n -Wshadow\n -Wwrite-strings\n -march=native\n # -MMD\n -std=c++11\n -rdynamic\n )\nif(CMAKE_BUILD_BITS EQUAL 32)\n  list(APPEND CXX_FLAGS \"-m32\")\nendif()\nif(CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n  list(APPEND CXX_FLAGS \"-Wno-null-dereference\")\n  list(APPEND CXX_FLAGS \"-Wno-sign-conversion\")\n  list(APPEND CXX_FLAGS \"-Wno-unused-local-typedef\")\n  list(APPEND CXX_FLAGS \"-Wthread-safety\")\n  list(REMOVE_ITEM CXX_FLAGS \"-rdynamic\")\nendif()\nstring(REPLACE \";\" \" \" CMAKE_CXX_FLAGS \"${CXX_FLAGS}\")\n\nset(CMAKE_CXX_FLAGS_DEBUG \"-O0\")\nset(CMAKE_CXX_FLAGS_RELEASE \"-O2 -DNDEBUG\")\nset(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)\nset(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)\n\nfind_package(Boost REQUIRED)\nfind_package(Protobuf)\nfind_package(CURL)\nfind_package(ZLIB)\nfind_path(CARES_INCLUDE_DIR ares.h)\nfind_library(CARES_LIBRARY NAMES cares)\nfind_path(MHD_INCLUDE_DIR microhttpd.h)\nfind_library(MHD_LIBRARY NAMES microhttpd)\nfind_library(BOOSTTEST_LIBRARY NAMES boost_unit_test_framework)\nfind_library(BOOSTPO_LIBRARY NAMES boost_program_options)\nfind_library(BOOSTSYSTEM_LIBRARY NAMES boost_system)\nfind_path(TCMALLOC_INCLUDE_DIR gperftools/heap-profiler.h)\nfind_library(TCMALLOC_LIBRARY NAMES tcmalloc_and_profiler)\nfind_path(HIREDIS_INCLUDE_DIR hiredis/hiredis.h)\nfind_library(HIREDIS_LIBRARY NAMES hiredis)\nfind_path(GD_INCLUDE_DIR gd.h)\nfind_library(GD_LIBRARY NAMES gd)\nfind_program(THRIFT_COMPILER thrift)\nfind_path(THRIFT_INCLUDE_DIR thrift)\nfind_library(THRIFT_LIBRARY NAMES thrift)\n\nif(CARES_INCLUDE_DIR AND CARES_LIBRARY)\n  message(STATUS \"found cares\")\nendif()\nif(CURL_FOUND)\n  message(STATUS \"found curl\")\nendif()\nif(PROTOBUF_FOUND)\n  message(STATUS \"found protobuf\")\nendif()\nif(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY)\n  message(STATUS \"found tcmalloc\")\nendif()\nif(ZLIB_FOUND)\n  message(STATUS \"found zlib\")\nendif()\nif(HIREDIS_INCLUDE_DIR AND HIREDIS_LIBRARY)\n  message(STATUS \"found hiredis\")\nendif()\nif(GD_INCLUDE_DIR AND GD_LIBRARY)\n  message(STATUS \"found gd\")\nendif()\nif(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY)\n  message(STATUS \"found thrift\")\nendif()\n\ninclude_directories(${Boost_INCLUDE_DIRS})\n\ninclude_directories(${PROJECT_SOURCE_DIR})\n\nstring(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)\nmessage(STATUS \"CXX_FLAGS = \" ${CMAKE_CXX_FLAGS} \" \" ${CMAKE_CXX_FLAGS_${BUILD_TYPE}})\n\nadd_subdirectory(muduo/base)\nadd_subdirectory(muduo/net)\n\nif(MUDUO_BUILD_EXAMPLES)\n  add_subdirectory(contrib)\n  add_subdirectory(examples)\nelse()\n  if(CARES_INCLUDE_DIR AND CARES_LIBRARY)\n    add_subdirectory(examples/cdns)\n  endif()\nendif()\n\n"
  },
  {
    "path": "ChangeLog",
    "content": "2018-10-22   Shuo Chen  <chenshuo@chenshuo.com>\n  * Last version in C++98/03, next version will use C++11\n  * Enable Clang Thread Safety Analysis.\n  * Fix \"off_t does not name a type\" for CentOS 7 (#316) by qiao hai-jun\n  * Fix warnings for gcc 8.\n  * Add ttcp asio examples\n  * Implement Procmon::listFiles()\n\n2018-01-18   Shuo Chen  <chenshuo@chenshuo.com>\n  * Fix race condition between Thread::tid() and Thread::start().\n  * Change protorpc format, add back package name. (Go does this since 1.2)\n  * examples/socks4a/tunnel.h stops reading if output buffer is full.\n  * Fixes for GCC 6/7\n  * Minor fixes by huntinux, liangshaocong, zhoudayang2, octocat_lee,\n  jack.xsuperman, ligewei, yqsy.\n  * Version 1.1.0\n\n2016-10-25   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add Travis CI\n  * Add EvevtLoop::queueSize() by <zhuangshi23>\n  * Implement TcpClient::retry() by fdxuwei\n  * Change Condition::waitForSeconds() parameter type from int to double by ChaoShu\n  * Minor fixes by JackDrogon, YuCong, zieckey, wuzhaogai\n  * Version 1.0.9\n\n2016-02-11   Shuo Chen  <chenshuo@chenshuo.com>\n  * Preliminary support of IPv6.\n  * Add stop/startRead in TcpConnection by <zhang.jako>\n  * Version 1.0.8\n\n2015-11-09   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add stats to Sudoku examples.\n  * Add example of PeriodicTimer class.\n  * Add thrift examples by <decimalbell>.\n  * Move hiredis example by <decimalbell> to contrib/.\n  * Move HTTP parseRequest to HttpContext class by <decimalbell>.\n  * Other fixes from <harrywong>, <cfreestar>, <qlhuangrui>, <lidw1988>.\n  * Version 1.0.7\n\n2015-04-03   Shuo Chen  <chenshuo@chenshuo.com>\n  * Fix ProcessInspector::threads().\n  * Minor fixes and improvements from liyuan989 and zieckey.\n  * More Sudoku examples.\n  * Version 1.0.6\n\n2015-01-30   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add examples/procmon\n  * EventLoop supports set/get context by <zieckey>\n  * Fix bug #107\n  * Version 1.0.5\n\n2014-10-05   Shuo Chen  <chenshuo@chenshuo.com>\n  * Enrich interfaces of EventLoopThreadPool by <zieckey>\n  * Buffer supports reading int64_t by <alisper>\n  * Add hiredis example by <decimalbell>\n  * Fix bug about TcpClient life time again.\n  * Other minor fixes, including some from <huahang>\n  * Version 1.0.4\n\n2014-08-02   Shuo Chen  <chenshuo@chenshuo.com>\n  * Singleton supports 'no_destroy'.\n  * Get tcp_info in TcpConnection.\n  * Add CurrentThread::tidStringLength().\n  * Fix bug about TcpClient life time. More checks.\n  * Version 1.0.3\n\n2014-06-30   Shuo Chen  <chenshuo@chenshuo.com>\n  * Fix boundary check in Buffer::findEOL() by <renxingsong>.\n  * Fix typos in InetAddress.cc by <huangml.zh>.\n  * Fix 32-bit integer overflow bug in time_client by <guochy2012>.\n  * Update comments in Buffer::readFd() by <huahang>.\n  * Add ThreadPool::setThreadInitCallback().\n  * Rename GzipStream to ZlibStream.\n  * Version 1.0.2\n\n2014-04-10   Shuo Chen  <chenshuo@chenshuo.com>\n  * More ProcessInfo functions.\n  * Add GzipFile (in C++11 only) and GzipOutputStream.\n  * Add SystemInspector.\n  * muduo::Threads now sets thread name with prctl().\n  * Version 1.0.1\n\n2014-03-12   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add TCP and RPC balancer examples\n  * Version 1.0.0\n\n2014-03-05   Shuo Chen  <chenshuo@chenshuo.com>\n  * Introduce class StringArg for passing C-style string arguments.\n  * Support localtime in logging.\n  * Version 1.0.0-rc2\n\n2014-02-22   Shuo Chen  <chenshuo@chenshuo.com>\n  * Default to release build.\n  * Version 1.0.0-rc1\n\n2014-02-22   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add base/WeakCallback.h\n  * Add TcpConnection::forceCloseWithDelay().\n  * Add InetAddress::resolve for sync DNS resolving.\n  * Add simple Protobuf codec for single message type.\n  * Add ACE ttcp and logging examples.\n  * Fix race conditoin in RpcChannel::CallMethod().\n  * Version 0.9.8\n\n2014-01-11   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add TcpConnection::forceClose().\n  * Add fastcgi nginx.conf example\n  * Fix iterator invalidation in hub.cc.\n  * Version 0.9.7\n\n2013-10-21   Shuo Chen  <chenshuo@chenshuo.com>\n  * Minor fixes.\n  * Version 0.9.6\n\n2013-08-31   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add C++11 rvalue overloads for boost::function parameters\n  * Add PerformanceInspector, support remote profiling with gperftools\n  * Add examples of memcached server and client\n  * Version 0.9.5\n\n2013-07-28   Shuo Chen  <chenshuo@chenshuo.com>\n  * Protobuf RPC wire protocol changed,\n    package name removed in 'service' field.\n  * Add roundtrip_udp as a UDP example\n  * More inspect\n  * Fix Connector::stop()\n  * Fix for protobuf 2.5.0\n  * Version 0.9.4\n\n2013-05-11   Shuo Chen  <chenshuo@chenshuo.com>\n  * ThreadPool can be blocking\n  * Support SO_REUSEPORT, added in kernel 3.9.0\n  * Fix Mutex::isLockedByThisThread()\n  * Version 0.9.3\n\n2013-03-22   Shuo Chen  <chenshuo@chenshuo.com>\n  * Fix bugs\n  * Add Sudoku client\n  * Version 0.9.2\n\n2013-01-16   Shuo Chen  <chenshuo@chenshuo.com>\n  * Fix bug introduced in dd26871\n  * Version 0.9.1\n\n2013-01-09   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add single thread concurrent download example in examples/curl.\n  * Add distributed word counting example.\n  * Add simple FastCGI example.\n  * Fix HttpRequest for empty header value, contributed by SeasonLee\n  * Fix Connector destruction\n  * Version 0.9.0\n\n2012-11-06   Shuo Chen  <chenshuo@chenshuo.com>\n  * Version for the book\n  * Fix Buffer::shrink()\n  * Fix race condition of ThreadPool::stop()\n  * Version 0.8.2\n\n2012-09-30   Shuo Chen  <chenshuo@chenshuo.com>\n  * Add Channel::remove()\n  * Logger::SourceFile supports char*\n  * Fix for g++ 4.7\n  * Version 0.8.1\n\n2012-09-06   Shuo Chen  <chenshuo@chenshuo.com>\n  * More Buffer member functions, contributed by SeasonLee\n  * Add unit tests for Buffer\n  * Fix wait condition in AsyncLogging::threadFunc()\n  * Rename fromHostPort to fromIpPort\n  * Add hash_value for shared_ptr\n  * Add TcpConnection::getMutableContext()\n  * Remove unnecessary code, header\n  * Add another example in idleconnection\n  * Version 0.8.0\n\n2012-06-26   Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add TimeZone class and unit tests.\n  * Inline Buffer::appendInt32() and Buffer::peekInt32().\n  * Catch exception in Thread::runInThread().\n    Rethrow in catch(...) to make pthread_cancel() working.\n  * Avoid deleting incomplete types.\n  * Replace delete with boost::ptr_vector\n  * Destructs ThreadLocalSingleton\n  * Replace __thread object with ThreadLocalSingleton in examples/asio/chat/\n  * Fix compile with g++ 4.6\n  * With armlinux.diff, muduo compiles on Raspberry Pi with g++ 4.5.\n  * Version 0.7.0\n\n2012-06-11   Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Put hostname as part of log file name.\n  * Extract muduo/base/CurrentThread.h\n  * Optimize logging for thread id and source filename.\n  * Add BlockingQueue_bench, improve Thread_bench.\n  * Add examples/zeromq, for round-trip latency tests.\n  * Demonstrate HighWaterMark callback and weak callback in tcp tunnel.\n  * Fix chat codec for invalid length.\n  * Version 0.6.0\n\n2012-06-03  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Replace std::ostream with LogStream.\n  * Add LogFile and AsyncLogging.\n  * Set SO_KEEPALIVE by default.\n  * Add HighWaterMark callback to TcpConnection.\n  * Add EventLoop::getEventLoopOfCurrentThread(),\n    Add ThreadInitCallback to EventLoopThreadPool.\n  * Add asio_chat_server_threaded_highperformance\n  * Version 0.5.0\n\n2012-05-18  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add FileUtil.\n  * Add new functions in ProcessInfo\n  * Add example for curl.\n  * Add add RPC meta service proto.\n  * Add loadtest for asio chat.\n  * Version 0.3.5\n\n2012-03-22  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add example for async rpc (resolver).\n  * Install muduo_cdns\n  * Version 0.3.4\n\n2012-03-16  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Remove net/protorpc2\n    moved to http://github.com/chenshuo/muduo-protorpc\n  * Install EventLoopThreadPool.h, rpc.proto and rpc.pb.h\n  * Version 0.3.3\n\n2012-03-11  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add asynchronous DNS stub resolver based on c-ares.\n    See also https://github.com/chenshuo/muduo-udns\n  * Replace string with StringPiece for function parameters.\n  * Change default log level from DEBUG to INFO,\n    set MUDUO_LOG_DEBUG=1 to revert.\n  * Install Channel.h\n  * Version 0.3.2\n\n2012-03-01  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Support multi-threaded http server.\n  * Do not install SocketsOps.h\n  * Version 0.3.1\n\n2012-02-24  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Support Keep-Alive for HTTP/1.0.\n  * Check return value of pthread_create.\n  * Minor fixes (set TcpNoDelay, stop() in ThreadPool::dtor)\n  * Version 0.3.0\n\n2011-09-18  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * EventLoop now supports cancelling timer.\n  * Add two examples of asio chat server, demo copy-on-write\n  in multithreaded program.\n  * Version 0.2.9\n\n2011-09-04  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Refactored RPC implementation of version 1 and 2,\n  programming interface differ, interoperable.\n  version 2 is incomplete yet.\n  * Find protobuf with cmake find_package().\n  * Version 0.2.8\n\n2011-09-03  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add a proof of concept implementation of Protobuf RPC.\n  * Version 0.2.7\n\n2011-06-27  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Fix decoding of Sudoku request.\n  * Backport to older Linux.\n  * Add BoundedBlockingQueue\n  * Version 0.2.6\n\n2011-06-15  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add examples/sudoku.\n  * Add thread benchmark.\n  * Version 0.2.5\n\n2011-06-02  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add examples/shorturl.\n  * Version 0.2.4\n\n2011-05-24  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Fix warnings on Arch Linux (GCC 4.6.0), thanks to ifreedom\n  * Add CMake install instructions, thanks to ifreedom\n  * Fix warnings on 32-bit Linux, thanks to highshow\n  * Version 0.2.3\n\n2011-05-15  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Changes from reactor tutorial\n  * Version 0.2.2\n\n2011-05-07  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Try making TcpClient destructable\n  * Add demux in examples/multiplexer\n  * Add examples/socks4a\n  * Changes for reactor tutorial\n  * Version 0.2.1\n\n2011-04-27  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add kick idle connection example in examples/idleconnection.\n  * Add test harness to examples/multiplexer\n  * Replace std::list with std::set in TimerQueue.\n  * Version 0.2.0\n\n2011-04-11  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add Google Protobuf codec and dispatcher\n  * Revert 'Add max connection limit to simple echo example.'\n  * Add max connection limit example in examples/maxconnection.\n  * Version 0.1.9\n\n2011-03-27  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add file transfer download examples.\n  * Add max connection limit to simple echo example.\n  * Make inputBuffer accessible in TcpConnection.\n  * Const-ness correct in Buffer class.\n  * Add Mutex test for benchmarking.\n  * Replace anonymous namespace with muduo::detail in muduo/base.\n  * Version 0.1.8\n\n2011-02-03  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Fix LengthHeaderCodec::onMessage() in examples/asio/chat.\n  * Version 0.1.7\n\n2011-02-01  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Fix onConnection() in simple examples.\n  * Reset t_cachedTid after fork().\n  * Version 0.1.6\n\n2010-12-15  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add examples/multiplexer\n  * Fix epoll kNoneEvent\n  * Version 0.1.5\n\n2010-11-20  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Fix retry logic\n  * Version 0.1.4\n\n2010-09-26  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Check SO_ERROR when connection is made.\n\n2010-09-11  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Gracefully refuse clients when accept(2) returns EMFILE.\n  * Version 0.1.3\n\n2010-09-07  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Libevent benchmark for event handling.\n  * Version 0.1.2\n\n2010-09-04  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Ping-pong benchmark, version 0.1.1\n\n2010-08-30  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * First pre-alpha release, version 0.1.0\n\n2010-08-29  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Sub works.\n\n2010-08-28  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add twisted finger examples.\n\n2010-08-27  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add simple chargen example.\n\n2010-08-07  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add Date.\n\n2010-05-15  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Hub works.\n\n2010-05-14  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Inspects opened files and threads.\n\n2010-05-11  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add inspector for process info.\n\n2010-05-04  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add simple http server and client.\n\n2010-04-25  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Add examples.\n\n2010-04-11  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * TcpClient works.\n\n2010-04-03  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * TcpServer works.\n\n2010-03-15  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * TcpConnection at server side works.\n\n2010-03-14  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Acceptor works.\n\n2010-03-13  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * TimerQueue works.\n\n2010-03-12  Shuo Chen  <chenshuo@chenshuo.com>\n\n  * Starts working on Muduo.\n"
  },
  {
    "path": "ChangeLog2",
    "content": "2018-10-24   Shuo Chen  <chenshuo@chenshuo.com>\n  * First release of C++11 version of muduo.\n  * Forked after v1.0.9, e6c04c43 is the base. changes in cpp98 branch are integrated\n  * Replace boost::shared_ptr/boost::weak_ptr with std::shared_ptr/std::weak_ptr.\n  * Replace boost::function/boost::bind with std::function/std::bind/lambda.\n  * Replace boost::ptr_vector<T> with std::vector<std::unique_ptr<T>>.\n  * Replace boost::noncopyable with muduo::noncopyable.\n  * Replace boost::scoped_ptr with std::unique_ptr.\n  * Replace BOOST_STATIC_ASSERT with static_assert.\n  * Replace boost type_traits with std type_traits.\n  * Version 2.0.0\n\n"
  },
  {
    "path": "License",
    "content": "// Muduo - A reactor-based C++ network library for Linux\n// Copyright (c) 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions\n// are met:\n//\n//   * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//   * Redistributions in binary form must reproduce the above copyright\n// notice, this list of conditions and the following disclaimer in the\n// documentation and/or other materials provided with the distribution.\n//   * Neither the name of Shuo Chen nor the names of other contributors\n// may be used to endorse or promote products derived from this software\n// without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n"
  },
  {
    "path": "README",
    "content": "Muduo is a multithreaded C++ network library based on\nthe reactor pattern.\n\nhttp://github.com/chenshuo/muduo\n\nCopyright (c) 2010, Shuo Chen.  All rights reserved.\n\nUse of this source code is governed by a BSD-style\nlicense that can be found in the License file.\n\nRequires:\n  Linux kernel version >= 2.6.28.\n  GCC >= 4.7 or Clang >= 3.5\n  Boost (for boost::any only.)\n\nTested on:\n  Debian 7 and above\n  Unbuntu 14.04 and above\n  CentOS 7 and above\n\nInstall required packages:\n  # Debian, Ubuntu, etc.\n  $ sudo apt install g++ cmake make libboost-dev\n  # CentOS\n  $ sudo yum install gcc-c++ cmake make boost-devel\n\nSee .travis.yml for additional packages for building more examples.\n\nTo build, run:\n  ./build.sh\n\nSee https://github.com/chenshuo/muduo-tutorial for\nhow to use muduo in your project.\n  __  __           _\n |  \\/  |         | |\n | \\  / |_   _  __| |_   _  ___\n | |\\/| | | | |/ _` | | | |/ _ \\\n | |  | | |_| | (_| | |_| | (_) |\n |_|  |_|\\__,_|\\__,_|\\__,_|\\___/\n\n"
  },
  {
    "path": "WORKSPACE",
    "content": ""
  },
  {
    "path": "build.sh",
    "content": "#!/bin/sh\n\nset -x\n\nSOURCE_DIR=`pwd`\nBUILD_DIR=${BUILD_DIR:-../build}\nBUILD_TYPE=${BUILD_TYPE:-release}\nINSTALL_DIR=${INSTALL_DIR:-../${BUILD_TYPE}-install-cpp11}\nCXX=${CXX:-g++}\n\nln -sf $BUILD_DIR/$BUILD_TYPE-cpp11/compile_commands.json\n\nmkdir -p $BUILD_DIR/$BUILD_TYPE-cpp11 \\\n  && cd $BUILD_DIR/$BUILD_TYPE-cpp11 \\\n  && cmake \\\n           -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n           -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR \\\n           -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \\\n           $SOURCE_DIR \\\n  && make $*\n\n# Use the following command to run all the unit tests\n# at the dir $BUILD_DIR/$BUILD_TYPE :\n# CTEST_OUTPUT_ON_FAILURE=TRUE make test\n\n# cd $SOURCE_DIR && doxygen\n\n"
  },
  {
    "path": "contrib/CMakeLists.txt",
    "content": "if(HIREDIS_INCLUDE_DIR AND HIREDIS_LIBRARY)\n  add_subdirectory(hiredis)\nelse()\n  add_subdirectory(hiredis EXCLUDE_FROM_ALL)\nendif()\n\nif(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY)\n  add_subdirectory(thrift)\nelse()\n  add_subdirectory(thrift EXCLUDE_FROM_ALL)\nendif()\n"
  },
  {
    "path": "contrib/hiredis/CMakeLists.txt",
    "content": "add_executable(mrediscli Hiredis.cc mrediscli.cc)\ntarget_link_libraries(mrediscli muduo_net hiredis)\n"
  },
  {
    "path": "contrib/hiredis/Hiredis.cc",
    "content": "#include \"contrib/hiredis/Hiredis.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <hiredis/async.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace hiredis;\n\nstatic void dummy(const std::shared_ptr<Channel>&)\n{\n}\n\nHiredis::Hiredis(EventLoop* loop, const InetAddress& serverAddr)\n  : loop_(loop),\n    serverAddr_(serverAddr),\n    context_(NULL)\n{\n}\n\nHiredis::~Hiredis()\n{\n  LOG_DEBUG << this;\n  assert(!channel_ || channel_->isNoneEvent());\n  ::redisAsyncFree(context_);\n}\n\nbool Hiredis::connected() const\n{\n  return channel_ && context_ && (context_->c.flags & REDIS_CONNECTED);\n}\n\nconst char* Hiredis::errstr() const\n{\n  assert(context_ != NULL);\n  return context_->errstr;\n}\n\nvoid Hiredis::connect()\n{\n  assert(!context_);\n\n  context_ = ::redisAsyncConnect(serverAddr_.toIp().c_str(), serverAddr_.port());\n\n  context_->ev.addRead = addRead;\n  context_->ev.delRead = delRead;\n  context_->ev.addWrite = addWrite;\n  context_->ev.delWrite = delWrite;\n  context_->ev.cleanup = cleanup;\n  context_->ev.data = this;\n\n  setChannel();\n\n  assert(context_->onConnect == NULL);\n  assert(context_->onDisconnect == NULL);\n  ::redisAsyncSetConnectCallback(context_, connectCallback);\n  ::redisAsyncSetDisconnectCallback(context_, disconnectCallback);\n}\n\nvoid Hiredis::disconnect()\n{\n  if (connected())\n  {\n    LOG_DEBUG << this;\n    ::redisAsyncDisconnect(context_);\n  }\n}\n\nint Hiredis::fd() const\n{\n  assert(context_);\n  return context_->c.fd;\n}\n\nvoid Hiredis::setChannel()\n{\n  LOG_DEBUG << this;\n  assert(!channel_);\n  channel_.reset(new Channel(loop_, fd()));\n  channel_->setReadCallback(std::bind(&Hiredis::handleRead, this, _1));\n  channel_->setWriteCallback(std::bind(&Hiredis::handleWrite, this));\n}\n\nvoid Hiredis::removeChannel()\n{\n  LOG_DEBUG << this;\n  channel_->disableAll();\n  channel_->remove();\n  loop_->queueInLoop(std::bind(dummy, channel_));\n  channel_.reset();\n}\n\nvoid Hiredis::handleRead(muduo::Timestamp receiveTime)\n{\n  LOG_TRACE << \"receiveTime = \" << receiveTime.toString();\n  ::redisAsyncHandleRead(context_);\n}\n\nvoid Hiredis::handleWrite()\n{\n  if (!(context_->c.flags & REDIS_CONNECTED))\n  {\n    removeChannel();\n  }\n  ::redisAsyncHandleWrite(context_);\n}\n\n/* static */ Hiredis* Hiredis::getHiredis(const redisAsyncContext* ac)\n{\n  Hiredis* hiredis = static_cast<Hiredis*>(ac->ev.data);\n  assert(hiredis->context_ == ac);\n  return hiredis;\n}\n\nvoid Hiredis::logConnection(bool up) const\n{\n  InetAddress localAddr(sockets::getLocalAddr(fd()));\n  InetAddress peerAddr(sockets::getPeerAddr(fd()));\n\n  LOG_INFO << localAddr.toIpPort() << \" -> \"\n           << peerAddr.toIpPort() << \" is \"\n           << (up ? \"UP\" : \"DOWN\");\n}\n\n/* static */ void Hiredis::connectCallback(const redisAsyncContext* ac, int status)\n{\n  LOG_TRACE;\n  getHiredis(ac)->connectCallback(status);\n}\n\nvoid Hiredis::connectCallback(int status)\n{\n  if (status != REDIS_OK)\n  {\n    LOG_ERROR << context_->errstr << \" failed to connect to \" << serverAddr_.toIpPort();\n  }\n  else\n  {\n    logConnection(true);\n    setChannel();\n  }\n\n  if (connectCb_)\n  {\n    connectCb_(this, status);\n  }\n}\n\n/* static */ void Hiredis::disconnectCallback(const redisAsyncContext* ac, int status)\n{\n  LOG_TRACE;\n  getHiredis(ac)->disconnectCallback(status);\n}\n\nvoid Hiredis::disconnectCallback(int status)\n{\n  logConnection(false);\n  removeChannel();\n\n  if (disconnectCb_)\n  {\n    disconnectCb_(this, status);\n  }\n}\n\nvoid Hiredis::addRead(void* privdata)\n{\n  LOG_TRACE;\n  Hiredis* hiredis = static_cast<Hiredis*>(privdata);\n  hiredis->channel_->enableReading();\n}\n\nvoid Hiredis::delRead(void* privdata)\n{\n  LOG_TRACE;\n  Hiredis* hiredis = static_cast<Hiredis*>(privdata);\n  hiredis->channel_->disableReading();\n}\n\nvoid Hiredis::addWrite(void* privdata)\n{\n  LOG_TRACE;\n  Hiredis* hiredis = static_cast<Hiredis*>(privdata);\n  hiredis->channel_->enableWriting();\n}\n\nvoid Hiredis::delWrite(void* privdata)\n{\n  LOG_TRACE;\n  Hiredis* hiredis = static_cast<Hiredis*>(privdata);\n  hiredis->channel_->disableWriting();\n}\n\nvoid Hiredis::cleanup(void* privdata)\n{\n  Hiredis* hiredis = static_cast<Hiredis*>(privdata);\n  LOG_DEBUG << hiredis;\n}\n\nint Hiredis::command(const CommandCallback& cb, muduo::StringArg cmd, ...)\n{\n  if (!connected()) return REDIS_ERR;\n\n  LOG_TRACE;\n  CommandCallback* p = new CommandCallback(cb);\n  va_list args;\n  va_start(args, cmd);\n  int ret = ::redisvAsyncCommand(context_, commandCallback, p, cmd.c_str(), args);\n  va_end(args);\n  return ret;\n}\n\n/* static */ void Hiredis::commandCallback(redisAsyncContext* ac, void* r, void* privdata)\n{\n  redisReply* reply = static_cast<redisReply*>(r);\n  CommandCallback* cb = static_cast<CommandCallback*>(privdata);\n  getHiredis(ac)->commandCallback(reply, cb);\n}\n\nvoid Hiredis::commandCallback(redisReply* reply, CommandCallback* cb)\n{\n  (*cb)(this, reply);\n  delete cb;\n}\n\nint Hiredis::ping()\n{\n  return command(std::bind(&Hiredis::pingCallback, this, _1, _2), \"PING\");\n}\n\nvoid Hiredis::pingCallback(Hiredis* me, redisReply* reply)\n{\n  assert(this == me);\n  LOG_DEBUG << reply->str;\n}\n"
  },
  {
    "path": "contrib/hiredis/Hiredis.h",
    "content": "#ifndef MUDUO_CONTRIB_HIREDIS_HIREDIS_H\n#define MUDUO_CONTRIB_HIREDIS_HIREDIS_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/Callbacks.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <hiredis/hiredis.h>\n\nstruct redisAsyncContext;\n\nnamespace muduo\n{\nnamespace net\n{\nclass Channel;\nclass EventLoop;\n}\n}\n\nnamespace hiredis\n{\n\nclass Hiredis : public std::enable_shared_from_this<Hiredis>,\n                muduo::noncopyable\n{\n public:\n  typedef std::function<void(Hiredis*, int)> ConnectCallback;\n  typedef std::function<void(Hiredis*, int)> DisconnectCallback;\n  typedef std::function<void(Hiredis*, redisReply*)> CommandCallback;\n\n  Hiredis(muduo::net::EventLoop* loop, const muduo::net::InetAddress& serverAddr);\n  ~Hiredis();\n\n  const muduo::net::InetAddress& serverAddress() const { return serverAddr_; }\n  // redisAsyncContext* context() { return context_; }\n  bool connected() const;\n  const char* errstr() const;\n\n  void setConnectCallback(const ConnectCallback& cb) { connectCb_ = cb; }\n  void setDisconnectCallback(const DisconnectCallback& cb) { disconnectCb_ = cb; }\n\n  void connect();\n  void disconnect();  // FIXME: implement this with redisAsyncDisconnect\n\n  int command(const CommandCallback& cb, muduo::StringArg cmd, ...);\n\n  int ping();\n\n private:\n  void handleRead(muduo::Timestamp receiveTime);\n  void handleWrite();\n\n  int fd() const;\n  void logConnection(bool up) const;\n  void setChannel();\n  void removeChannel();\n\n  void connectCallback(int status);\n  void disconnectCallback(int status);\n  void commandCallback(redisReply* reply, CommandCallback* privdata);\n\n  static Hiredis* getHiredis(const redisAsyncContext* ac);\n\n  static void connectCallback(const redisAsyncContext* ac, int status);\n  static void disconnectCallback(const redisAsyncContext* ac, int status);\n  // command callback\n  static void commandCallback(redisAsyncContext* ac, void*, void*);\n\n  static void addRead(void* privdata);\n  static void delRead(void* privdata);\n  static void addWrite(void* privdata);\n  static void delWrite(void* privdata);\n  static void cleanup(void* privdata);\n\n  void pingCallback(Hiredis* me, redisReply* reply);\n\n private:\n  muduo::net::EventLoop* loop_;\n  const muduo::net::InetAddress serverAddr_;\n  redisAsyncContext* context_;\n  std::shared_ptr<muduo::net::Channel> channel_;\n  ConnectCallback connectCb_;\n  DisconnectCallback disconnectCb_;\n};\n\n}  // namespace hiredis\n\n#endif  // MUDUO_CONTRIB_HIREDIS_HIREDIS_H\n"
  },
  {
    "path": "contrib/hiredis/README.md",
    "content": "# Hiredis\n\nThe version of hiredis must be 0.11.0 or greater\n\nSee also issue [#92](https://github.com/chenshuo/muduo/issues/92)\n"
  },
  {
    "path": "contrib/hiredis/mrediscli.cc",
    "content": "#include \"contrib/hiredis/Hiredis.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <string>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstring toString(long long value)\n{\n  char buf[32];\n  snprintf(buf, sizeof buf, \"%lld\", value);\n  return buf;\n}\n\nstring redisReplyToString(const redisReply* reply)\n{\n  static const char* const types[] = { \"\",\n      \"REDIS_REPLY_STRING\", \"REDIS_REPLY_ARRAY\",\n      \"REDIS_REPLY_INTEGER\", \"REDIS_REPLY_NIL\",\n      \"REDIS_REPLY_STATUS\", \"REDIS_REPLY_ERROR\" };\n  string str;\n  if (!reply) return str;\n\n  str += types[reply->type] + string(\"(\") + toString(reply->type) + \") \";\n\n  str += \"{ \";\n  if (reply->type == REDIS_REPLY_STRING ||\n      reply->type == REDIS_REPLY_STATUS ||\n      reply->type == REDIS_REPLY_ERROR)\n  {\n    str += '\"' + string(reply->str, reply->len) + '\"';\n  }\n  else if (reply->type == REDIS_REPLY_INTEGER)\n  {\n    str += toString(reply->integer);\n  }\n  else if (reply->type == REDIS_REPLY_ARRAY)\n  {\n    str += toString(reply->elements) + \" \";\n    for (size_t i = 0; i < reply->elements; i++)\n    {\n      str += \" \" + redisReplyToString(reply->element[i]);\n    }\n  }\n  str += \" }\";\n\n  return str;\n}\n\nvoid connectCallback(hiredis::Hiredis* c, int status)\n{\n  if (status != REDIS_OK)\n  {\n    LOG_ERROR << \"connectCallback Error:\" << c->errstr();\n  }\n  else\n  {\n    LOG_INFO << \"Connected...\";\n  }\n}\n\nvoid disconnectCallback(hiredis::Hiredis* c, int status)\n{\n  if (status != REDIS_OK)\n  {\n    LOG_ERROR << \"disconnectCallback Error:\" << c->errstr();\n  }\n  else\n  {\n    LOG_INFO << \"Disconnected...\";\n  }\n}\n\nvoid timeCallback(hiredis::Hiredis* c, redisReply* reply)\n{\n  LOG_INFO << \"time \" << redisReplyToString(reply);\n}\n\nvoid echoCallback(hiredis::Hiredis* c, redisReply* reply, string* echo)\n{\n  LOG_INFO << *echo << \" \" << redisReplyToString(reply);\n  c->disconnect();\n}\n\nvoid dbsizeCallback(hiredis::Hiredis* c, redisReply* reply)\n{\n  LOG_INFO << \"dbsize \" << redisReplyToString(reply);\n}\n\nvoid selectCallback(hiredis::Hiredis* c, redisReply* reply, uint16_t* index)\n{\n  LOG_INFO << \"select \" << *index << \" \" << redisReplyToString(reply);\n}\n\nvoid authCallback(hiredis::Hiredis* c, redisReply* reply, string* password)\n{\n  LOG_INFO << \"auth \" << *password << \" \" << redisReplyToString(reply);\n}\n\nvoid echo(hiredis::Hiredis* c, string* s)\n{\n  c->command(std::bind(echoCallback, _1, _2, s), \"echo %s\", s->c_str());\n}\n\nint main(int argc, char** argv)\n{\n  Logger::setLogLevel(Logger::DEBUG);\n\n  EventLoop loop;\n\n  InetAddress serverAddr(\"127.0.0.1\", 6379);\n  hiredis::Hiredis hiredis(&loop, serverAddr);\n\n  hiredis.setConnectCallback(connectCallback);\n  hiredis.setDisconnectCallback(disconnectCallback);\n  hiredis.connect();\n\n  //hiredis.ping();\n  loop.runEvery(1.0, std::bind(&hiredis::Hiredis::ping, &hiredis));\n\n  hiredis.command(timeCallback, \"time\");\n\n  string hi = \"hi\";\n  hiredis.command(std::bind(echoCallback, _1, _2, &hi), \"echo %s\", hi.c_str());\n  loop.runEvery(2.0, std::bind(echo, &hiredis, &hi));\n\n  hiredis.command(dbsizeCallback, \"dbsize\");\n\n  uint16_t index = 8;\n  hiredis.command(std::bind(selectCallback, _1, _2, &index), \"select %d\", index);\n\n  string password = \"password\";\n  hiredis.command(std::bind(authCallback, _1, _2, &password), \"auth %s\", password.c_str());\n\n  loop.loop();\n\n  return 0;\n}\n"
  },
  {
    "path": "contrib/thrift/CMakeLists.txt",
    "content": "set(MUDUO_THRIFT_SRCS\n    ThriftConnection.cc\n    ThriftServer.cc\n    )\nadd_library(muduo_thrift ${MUDUO_THRIFT_SRCS})\ntarget_link_libraries(muduo_thrift muduo_net thrift)\n\nif(THRIFT_COMPILER AND THRIFT_INCLUDE_DIR AND THRIFT_LIBRARY)\n  include_directories(${CMAKE_CURRENT_SOURCE_DIR})\n  add_subdirectory(tests)\nendif()\n"
  },
  {
    "path": "contrib/thrift/ThriftConnection.cc",
    "content": "#include \"contrib/thrift/ThriftConnection.h\"\n\n#include <functional>\n\n#include \"muduo/base/Logging.h\"\n\n#include <thrift/transport/TTransportException.h>\n\n#include \"contrib/thrift/ThriftServer.h\"\n\nusing muduo::Timestamp;\nusing muduo::net::Buffer;\nusing muduo::net::TcpConnectionPtr;\nusing muduo::_1;\nusing muduo::_2;\nusing muduo::_3;\n\nThriftConnection::ThriftConnection(ThriftServer* server,\n                                  const TcpConnectionPtr& conn)\n  : server_(server),\n    conn_(conn),\n    state_(kExpectFrameSize),\n    frameSize_(0)\n{\n  conn_->setMessageCallback(std::bind(&ThriftConnection::onMessage,\n                                        this, _1, _2, _3));\n  nullTransport_.reset(new TNullTransport());\n  inputTransport_.reset(new TMemoryBuffer(NULL, 0));\n  outputTransport_.reset(new TMemoryBuffer());\n\n  factoryInputTransport_ = server_->getInputTransportFactory()->getTransport(inputTransport_);\n  factoryOutputTransport_ = server_->getOutputTransportFactory()->getTransport(outputTransport_);\n\n  inputProtocol_ = server_->getInputProtocolFactory()->getProtocol(factoryInputTransport_);\n  outputProtocol_ = server_->getOutputProtocolFactory()->getProtocol(factoryOutputTransport_);\n\n  processor_ = server_->getProcessor(inputProtocol_, outputProtocol_, nullTransport_);\n}\n\nvoid ThriftConnection::onMessage(const TcpConnectionPtr& conn,\n                                 Buffer* buffer,\n                                 Timestamp receiveTime)\n{\n  bool more = true;\n  while (more)\n  {\n    if (state_ == kExpectFrameSize)\n    {\n      if (buffer->readableBytes() >= 4)\n      {\n        frameSize_ = static_cast<uint32_t>(buffer->readInt32());\n        state_ = kExpectFrame;\n      }\n      else\n      {\n        more = false;\n      }\n    }\n    else if (state_ == kExpectFrame)\n    {\n      if (buffer->readableBytes() >= frameSize_)\n      {\n        uint8_t* buf = reinterpret_cast<uint8_t*>((const_cast<char*>(buffer->peek())));\n\n        inputTransport_->resetBuffer(buf, frameSize_, TMemoryBuffer::COPY);\n        outputTransport_->resetBuffer();\n        outputTransport_->getWritePtr(4);\n        outputTransport_->wroteBytes(4);\n\n        if (server_->isWorkerThreadPoolProcessing())\n        {\n          server_->workerThreadPool().run(std::bind(&ThriftConnection::process, this));\n        }\n        else\n        {\n          process();\n        }\n\n        buffer->retrieve(frameSize_);\n        state_ = kExpectFrameSize;\n      }\n      else\n      {\n        more = false;\n      }\n    }\n  }\n}\n\nvoid ThriftConnection::process()\n{\n  try\n  {\n    processor_->process(inputProtocol_, outputProtocol_, NULL);\n\n    uint8_t* buf;\n    uint32_t size;\n    outputTransport_->getBuffer(&buf, &size);\n\n    assert(size >= 4);\n    uint32_t frameSize = static_cast<uint32_t>(htonl(size - 4));\n    memcpy(buf, &frameSize, 4);\n\n    conn_->send(buf, size);\n  } catch (const TTransportException& ex)\n  {\n    LOG_ERROR << \"ThriftServer TTransportException: \" << ex.what();\n    close();\n  } catch (const std::exception& ex)\n  {\n    LOG_ERROR << \"ThriftServer std::exception: \" << ex.what();\n    close();\n  } catch (...)\n  {\n    LOG_ERROR << \"ThriftServer unknown exception\";\n    close();\n  }\n}\n\nvoid ThriftConnection::close()\n{\n  nullTransport_->close();\n  factoryInputTransport_->close();\n  factoryOutputTransport_->close();\n}\n"
  },
  {
    "path": "contrib/thrift/ThriftConnection.h",
    "content": "#ifndef MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H\n#define MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H\n\n#include <boost/enable_shared_from_this.hpp>\n#include <boost/noncopyable.hpp>\n\n#include \"muduo/net/TcpConnection.h\"\n\n#include <thrift/protocol/TProtocol.h>\n#include <thrift/transport/TBufferTransports.h>\n#include <thrift/transport/TTransportUtils.h>\n\nusing apache::thrift::TProcessor;\nusing apache::thrift::protocol::TProtocol;\nusing apache::thrift::transport::TMemoryBuffer;\nusing apache::thrift::transport::TNullTransport;\nusing apache::thrift::transport::TTransport;\nusing apache::thrift::transport::TTransportException;\n\nclass ThriftServer;\n\nclass ThriftConnection : boost::noncopyable,\n                         public boost::enable_shared_from_this<ThriftConnection>\n{\n public:\n  enum State\n  {\n    kExpectFrameSize,\n    kExpectFrame\n  };\n\n  ThriftConnection(ThriftServer* server, const muduo::net::TcpConnectionPtr& conn);\n\n private:\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buffer,\n                 muduo::Timestamp receiveTime);\n\n  void process();\n\n  void close();\n\n private:\n  ThriftServer* server_;\n  muduo::net::TcpConnectionPtr conn_;\n\n  boost::shared_ptr<TNullTransport> nullTransport_;\n\n  boost::shared_ptr<TMemoryBuffer> inputTransport_;\n  boost::shared_ptr<TMemoryBuffer> outputTransport_;\n\n  boost::shared_ptr<TTransport> factoryInputTransport_;\n  boost::shared_ptr<TTransport> factoryOutputTransport_;\n\n  boost::shared_ptr<TProtocol> inputProtocol_;\n  boost::shared_ptr<TProtocol> outputProtocol_;\n\n  boost::shared_ptr<TProcessor> processor_;\n\n  enum State state_;\n  uint32_t frameSize_;\n};\n\ntypedef boost::shared_ptr<ThriftConnection> ThriftConnectionPtr;\n\n#endif  // MUDUO_CONTRIB_THRIFT_THRIFTCONNECTION_H\n"
  },
  {
    "path": "contrib/thrift/ThriftServer.cc",
    "content": "#include \"contrib/thrift/ThriftServer.h\"\n\n#include <functional>\n\n#include \"muduo/net/EventLoop.h\"\n\nusing muduo::MutexLockGuard;\nusing muduo::Timestamp;\nusing muduo::net::EventLoop;\nusing muduo::net::TcpConnectionPtr;\n\nThriftServer::~ThriftServer() = default;\n\nvoid ThriftServer::serve()\n{\n  start();\n}\n\nvoid ThriftServer::start()\n{\n  if (numWorkerThreads_ > 0)\n  {\n    workerThreadPool_.start(numWorkerThreads_);\n  }\n  server_.start();\n}\n\nvoid ThriftServer::stop()\n{\n  if (numWorkerThreads_ > 0)\n  {\n    workerThreadPool_.stop();\n  }\n  server_.getLoop()->runAfter(3.0, std::bind(&EventLoop::quit,\n                                               server_.getLoop()));\n}\n\nvoid ThriftServer::onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    ThriftConnectionPtr ptr(new ThriftConnection(this, conn));\n    MutexLockGuard lock(mutex_);\n    assert(conns_.find(conn->name()) == conns_.end());\n    conns_[conn->name()] = ptr;\n  }\n  else\n  {\n    MutexLockGuard lock(mutex_);\n    assert(conns_.find(conn->name()) != conns_.end());\n    conns_.erase(conn->name());\n  }\n}\n"
  },
  {
    "path": "contrib/thrift/ThriftServer.h",
    "content": "#ifndef MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H\n#define MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H\n\n#include <functional>\n#include <map>\n\n#include <boost/noncopyable.hpp>\n\n#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <thrift/server/TServer.h>\n\n#include \"contrib/thrift/ThriftConnection.h\"\n\nusing apache::thrift::TProcessor;\nusing apache::thrift::TProcessorFactory;\nusing apache::thrift::protocol::TProtocolFactory;\nusing apache::thrift::server::TServer;\nusing apache::thrift::transport::TTransportFactory;\n\nclass ThriftServer : boost::noncopyable,\n                     public TServer\n{\n public:\n  ThriftServer(const boost::shared_ptr<TProcessorFactory>& processorFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processorFactory),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessor>& processor,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processor),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessorFactory>& processorFactory,\n               const boost::shared_ptr<TProtocolFactory>& protocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processorFactory),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputProtocolFactory(protocolFactory);\n    setOutputProtocolFactory(protocolFactory);\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessor>& processor,\n               const boost::shared_ptr<TProtocolFactory>& protocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processor),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputProtocolFactory(protocolFactory);\n    setOutputProtocolFactory(protocolFactory);\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessorFactory>& processorFactory,\n               const boost::shared_ptr<TTransportFactory>& transportFactory,\n               const boost::shared_ptr<TProtocolFactory>& protocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processorFactory),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputTransportFactory(transportFactory);\n    setOutputTransportFactory(transportFactory);\n    setInputProtocolFactory(protocolFactory);\n    setOutputProtocolFactory(protocolFactory);\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessor>& processor,\n               const boost::shared_ptr<TTransportFactory>& transportFactory,\n               const boost::shared_ptr<TProtocolFactory>& protocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processor),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputTransportFactory(transportFactory);\n    setOutputTransportFactory(transportFactory);\n    setInputProtocolFactory(protocolFactory);\n    setOutputProtocolFactory(protocolFactory);\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessorFactory>& processorFactory,\n               const boost::shared_ptr<TTransportFactory>& inputTransportFactory,\n               const boost::shared_ptr<TTransportFactory>& outputTransportFactory,\n               const boost::shared_ptr<TProtocolFactory>& inputProtocolFactory,\n               const boost::shared_ptr<TProtocolFactory>& outputProtocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processorFactory),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputTransportFactory(inputTransportFactory);\n    setOutputTransportFactory(outputTransportFactory);\n    setInputProtocolFactory(inputProtocolFactory);\n    setOutputProtocolFactory(outputProtocolFactory);\n  }\n\n  ThriftServer(const boost::shared_ptr<TProcessor>& processor,\n               const boost::shared_ptr<TTransportFactory>& inputTransportFactory,\n               const boost::shared_ptr<TTransportFactory>& outputTransportFactory,\n               const boost::shared_ptr<TProtocolFactory>& inputProtocolFactory,\n               const boost::shared_ptr<TProtocolFactory>& outputProtocolFactory,\n               muduo::net::EventLoop* eventloop,\n               const muduo::net::InetAddress& addr,\n               const muduo::string& name)\n    : TServer(processor),\n      server_(eventloop, addr, name),\n      numWorkerThreads_(0),\n      workerThreadPool_(name + muduo::string(\"WorkerThreadPool\"))\n  {\n    server_.setConnectionCallback(std::bind(&ThriftServer::onConnection,\n                                              this, muduo::_1));\n    setInputTransportFactory(inputTransportFactory);\n    setOutputTransportFactory(outputTransportFactory);\n    setInputProtocolFactory(inputProtocolFactory);\n    setOutputProtocolFactory(outputProtocolFactory);\n  }\n\n  virtual ~ThriftServer();\n\n  void serve();\n\n  void start();\n\n  void stop();\n\n  muduo::ThreadPool& workerThreadPool()\n  {\n    return workerThreadPool_;\n  }\n\n  bool isWorkerThreadPoolProcessing() const\n  {\n    return numWorkerThreads_ != 0;\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void setWorkerThreadNum(int numWorkerThreads)\n  {\n    assert(numWorkerThreads > 0);\n    numWorkerThreads_ = numWorkerThreads;\n  }\n\n private:\n  friend class ThriftConnection;\n\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n private:\n  muduo::net::TcpServer server_;\n  int numWorkerThreads_;\n  muduo::ThreadPool workerThreadPool_;\n  muduo::MutexLock mutex_;\n  std::map<muduo::string, ThriftConnectionPtr> conns_;\n};\n\n#endif  // MUDUO_CONTRIB_THRIFT_THRIFTSERVER_H\n"
  },
  {
    "path": "contrib/thrift/tests/.gitignore",
    "content": "gen-cpp\ngen-py\n"
  },
  {
    "path": "contrib/thrift/tests/CMakeLists.txt",
    "content": "add_subdirectory(echo)\nadd_subdirectory(ping)\n"
  },
  {
    "path": "contrib/thrift/tests/echo/CMakeLists.txt",
    "content": "include_directories(gen-cpp)\nset(ECHO_THRIFT echo.thrift)\nexecute_process(COMMAND ${THRIFT_COMPILER} --gen cpp ${ECHO_THRIFT}\n                WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})\nexecute_process(COMMAND ${THRIFT_COMPILER} --gen py ${ECHO_THRIFT}\n                WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})\nset(ECHO_THRIFT_SRCS\n    gen-cpp/echo_constants.cpp\n    gen-cpp/echo_types.cpp\n    gen-cpp/Echo.cpp\n    )\nset(ECHO_SRCS\n    EchoServer.cc\n    )\nadd_executable(muduo_thrift_echo ${ECHO_SRCS} ${ECHO_THRIFT_SRCS})\ntarget_link_libraries(muduo_thrift_echo muduo_thrift)\n"
  },
  {
    "path": "contrib/thrift/tests/echo/EchoServer.cc",
    "content": "#include <unistd.h>\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include \"ThriftServer.h\"\n\n#include \"Echo.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nusing namespace echo;\n\nclass EchoHandler : virtual public EchoIf\n{\n public:\n  EchoHandler()\n  {\n  }\n\n  void echo(std::string& str, const std::string& s)\n  {\n    LOG_INFO << \"EchoHandler::echo:\" << s;\n    str = s;\n  }\n\n};\n\nint NumCPU()\n{\n  return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));\n}\n\nint main(int argc, char **argv)\n{\n  EventLoop eventloop;\n  InetAddress addr(\"127.0.0.1\", 9090);\n  string name(\"EchoServer\");\n\n  boost::shared_ptr<EchoHandler> handler(new EchoHandler());\n  boost::shared_ptr<TProcessor> processor(new EchoProcessor(handler));\n\n  ThriftServer server(processor, &eventloop, addr, name);\n  server.setWorkerThreadNum(NumCPU() * 2);\n  server.start();\n  eventloop.loop();\n\n  return 0;\n}\n\n"
  },
  {
    "path": "contrib/thrift/tests/echo/echo.thrift",
    "content": "namespace cpp echo\nnamespace py echo\n\nservice Echo\n{\n  string echo(1: string arg);\n}\n\n"
  },
  {
    "path": "contrib/thrift/tests/echo/echoclient.py",
    "content": "import sys\nsys.path.append('gen-py')\n\nfrom thrift.transport import TSocket\nfrom thrift.transport import TTransport\nfrom thrift.protocol import TBinaryProtocol\n\nfrom echo import Echo\n\n\ndef echo(s):\n    transport = TSocket.TSocket('127.0.0.1', 9090)\n    tranport = TTransport.TFramedTransport(transport)\n    protocol = TBinaryProtocol.TBinaryProtocol(tranport)\n    client = Echo.Client(protocol)\n    tranport.open()\n    s = client.echo(s)\n    tranport.close()\n\n    return s\n\n\ndef main():\n    print(echo('42'))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "contrib/thrift/tests/ping/CMakeLists.txt",
    "content": "include_directories(gen-cpp)\nset(PING_THRIFT ping.thrift)\nexecute_process(COMMAND ${THRIFT_COMPILER} --gen cpp ${PING_THRIFT}\n                WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})\nexecute_process(COMMAND ${THRIFT_COMPILER} --gen py ${PING_THRIFT}\n                WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})\nset(PING_THRIFT_SRCS\n    gen-cpp/ping_constants.cpp\n    gen-cpp/ping_types.cpp\n    gen-cpp/Ping.cpp\n    )\nset(PING_SRCS\n    PingServer.cc\n    )\nadd_executable(muduo_thrift_ping ${PING_SRCS} ${PING_THRIFT_SRCS})\ntarget_link_libraries(muduo_thrift_ping muduo_thrift)\n"
  },
  {
    "path": "contrib/thrift/tests/ping/PingServer.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <thrift/protocol/TCompactProtocol.h>\n\n#include \"ThriftServer.h\"\n\n#include \"Ping.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nusing apache::thrift::protocol::TCompactProtocolFactory;\n\nusing namespace ping;\n\nclass PingHandler : virtual public PingIf\n{\n public:\n  PingHandler()\n  {\n  }\n\n  void ping()\n  {\n    LOG_INFO << \"ping\";\n  }\n\n};\n\nint main(int argc, char **argv)\n{\n  EventLoop eventloop;\n  InetAddress addr(\"127.0.0.1\", 9090);\n  string name(\"PingServer\");\n\n  boost::shared_ptr<PingHandler> handler(new PingHandler());\n  boost::shared_ptr<TProcessor> processor(new PingProcessor(handler));\n  boost::shared_ptr<TProtocolFactory> protcolFactory(new TCompactProtocolFactory());\n\n  ThriftServer server(processor, protcolFactory, &eventloop, addr, name);\n  server.start();\n  eventloop.loop();\n\n  return 0;\n}\n\n"
  },
  {
    "path": "contrib/thrift/tests/ping/ping.thrift",
    "content": "namespace cpp ping\nnamespace py ping\n\nservice Ping\n{\n  void ping();\n}\n\n"
  },
  {
    "path": "contrib/thrift/tests/ping/pingclient.py",
    "content": "import sys\nsys.path.append('gen-py')\n\nfrom thrift.transport import TSocket\nfrom thrift.transport import TTransport\nfrom thrift.protocol import TCompactProtocol\n\nfrom ping import Ping\n\n\ndef ping():\n    transport = TSocket.TSocket('127.0.0.1', 9090)\n    tranport = TTransport.TFramedTransport(transport)\n    protocol = TCompactProtocol.TCompactProtocol(tranport)\n    client = Ping.Client(protocol)\n    tranport.open()\n    client.ping()\n    tranport.close()\n\n\ndef main():\n    ping()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "examples/CMakeLists.txt",
    "content": "add_subdirectory(ace/ttcp)\nadd_subdirectory(asio/chat)\nadd_subdirectory(asio/tutorial)\nadd_subdirectory(fastcgi)\nadd_subdirectory(filetransfer)\nadd_subdirectory(hub)\nadd_subdirectory(idleconnection)\nadd_subdirectory(maxconnection)\nadd_subdirectory(memcached/client)\nadd_subdirectory(memcached/server)\nadd_subdirectory(multiplexer)\nadd_subdirectory(netty/discard)\nadd_subdirectory(netty/echo)\nadd_subdirectory(netty/uptime)\nadd_subdirectory(pingpong)\nadd_subdirectory(roundtrip)\nadd_subdirectory(shorturl)\nadd_subdirectory(simple)\nadd_subdirectory(socks4a)\nadd_subdirectory(sudoku)\nadd_subdirectory(twisted/finger)\nadd_subdirectory(wordcount)\nadd_subdirectory(zeromq)\n\nif(CARES_INCLUDE_DIR AND CARES_LIBRARY)\n  add_subdirectory(cdns)\nelse()\n  add_subdirectory(cdns EXCLUDE_FROM_ALL)\nendif()\n\nif(CURL_FOUND)\n  add_subdirectory(curl)\nelse()\n  add_subdirectory(curl EXCLUDE_FROM_ALL)\nendif()\n\nif(PROTOBUF_FOUND)\n  add_subdirectory(ace/logging)\n  add_subdirectory(protobuf)\nelse()\n  add_subdirectory(ace/logging EXCLUDE_FROM_ALL)\n  add_subdirectory(protobuf EXCLUDE_FROM_ALL)\nendif()\n\nif(GD_INCLUDE_DIR AND GD_LIBRARY)\n  add_subdirectory(procmon)\nelse()\n  add_subdirectory(procmon EXCLUDE_FROM_ALL)\nendif()\n"
  },
  {
    "path": "examples/ace/logging/CMakeLists.txt",
    "content": "add_custom_command(OUTPUT logrecord.pb.cc logrecord.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/logrecord.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS logrecord.proto\n  VERBATIM )\n\nset_source_files_properties(logrecord.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion -Wno-shadow\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(ace_logging_proto logrecord.pb.cc)\ntarget_link_libraries(ace_logging_proto protobuf pthread)\n\nadd_executable(ace_logging_client client.cc)\nset_target_properties(ace_logging_client PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(ace_logging_client muduo_protobuf_codec ace_logging_proto)\n\nadd_executable(ace_logging_server server.cc)\nset_target_properties(ace_logging_server PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(ace_logging_server muduo_protobuf_codec ace_logging_proto)\n"
  },
  {
    "path": "examples/ace/logging/client.cc",
    "content": "#include \"examples/ace/logging/logrecord.pb.h\"\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/protobuf/ProtobufCodecLite.h\"\n\n#include <iostream>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n// just to verify the protocol, not for practical usage.\n\nnamespace logging\n{\nextern const char logtag[] = \"LOG0\";\ntypedef ProtobufCodecLiteT<LogRecord, logtag> Codec;\n\n// same as asio/char/client.cc\nclass LogClient : noncopyable\n{\n public:\n  LogClient(EventLoop* loop, const InetAddress& serverAddr)\n    : client_(loop, serverAddr, \"LogClient\"),\n      codec_(std::bind(&LogClient::onMessage, this, _1, _2, _3))\n  {\n    client_.setConnectionCallback(\n        std::bind(&LogClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&Codec::onMessage, &codec_, _1, _2, _3));\n    client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void disconnect()\n  {\n    client_.disconnect();\n  }\n\n  void write(const StringPiece& message)\n  {\n    MutexLockGuard lock(mutex_);\n    updateLogRecord(message);\n    if (connection_)\n    {\n      codec_.send(connection_, logRecord_);\n    }\n    else\n    {\n      LOG_WARN << \"NOT CONNECTED\";\n    }\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    MutexLockGuard lock(mutex_);\n    if (conn->connected())\n    {\n      connection_ = conn;\n      LogRecord_Heartbeat* hb = logRecord_.mutable_heartbeat();\n      hb->set_hostname(ProcessInfo::hostname().c_str());\n      hb->set_process_name(ProcessInfo::procname().c_str());\n      hb->set_process_id(ProcessInfo::pid());\n      hb->set_process_start_time(ProcessInfo::startTime().microSecondsSinceEpoch());\n      hb->set_username(ProcessInfo::username().c_str());\n      updateLogRecord(\"Heartbeat\");\n      codec_.send(connection_, logRecord_);\n      logRecord_.clear_heartbeat();\n      LOG_INFO << \"Type message below:\";\n    }\n    else\n    {\n      connection_.reset();\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr&,\n                 const MessagePtr& message,\n                 Timestamp)\n  {\n    // SHOULD NOT HAPPEN\n    LogRecord* logRecord = muduo::down_cast<LogRecord*>(message.get());\n    LOG_WARN << logRecord->DebugString();\n  }\n\n  void updateLogRecord(const StringPiece& message) REQUIRES(mutex_)\n  {\n    mutex_.assertLocked();\n    logRecord_.set_level(1);\n    logRecord_.set_thread_id(CurrentThread::tid());\n    logRecord_.set_timestamp(Timestamp::now().microSecondsSinceEpoch());\n    logRecord_.set_message(message.data(), message.size());\n  }\n\n  TcpClient client_;\n  Codec codec_;\n  MutexLock mutex_;\n  LogRecord logRecord_ GUARDED_BY(mutex_);\n  TcpConnectionPtr connection_ GUARDED_BY(mutex_);\n};\n\n}  // namespace logging\n\nint main(int argc, char* argv[])\n{\n  if (argc < 3)\n  {\n    printf(\"usage: %s server_ip server_port\\n\", argv[0]);\n  }\n  else\n  {\n    EventLoopThread loopThread;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(argv[1], port);\n\n    logging::LogClient client(loopThread.startLoop(), serverAddr);\n    client.connect();\n    std::string line;\n    while (std::getline(std::cin, line))\n    {\n      client.write(line);\n    }\n    client.disconnect();\n    CurrentThread::sleepUsec(1000*1000);  // wait for disconnect, then safe to destruct LogClient (esp. TcpClient). Otherwise mutex_ is used after dtor.\n  }\n  google::protobuf::ShutdownProtobufLibrary();\n}\n"
  },
  {
    "path": "examples/ace/logging/logrecord.proto",
    "content": "package logging;\n\nmessage LogRecord {\n  // must present in first message\n  message Heartbeat {\n    required string hostname = 1;\n    required string process_name = 2;\n    required int32 process_id = 3;\n    required int64 process_start_time = 4; // microseconds sinch epoch\n    required string username = 5;\n  }\n\n  optional Heartbeat heartbeat = 1;\n  // muduo/base/Logging.h\n  // enum LogLevel\n  // {\n  //   TRACE, // 0\n  //   DEBUG, // 1\n  //   INFO,  // 2\n  //   WARN,  // 3\n  //   ERROR, // 4\n  //   FATAL, // 5\n  // };\n  required int32 level = 2;\n  required int32 thread_id = 3;\n  required int64 timestamp = 4; // microseconds sinch epoch\n  required string message = 5;\n  // optional: source file, source line, function name\n}\n"
  },
  {
    "path": "examples/ace/logging/server.cc",
    "content": "#include \"examples/ace/logging/logrecord.pb.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"muduo/net/protobuf/ProtobufCodecLite.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace logging\n{\nextern const char logtag[] = \"LOG0\";\ntypedef ProtobufCodecLiteT<LogRecord, logtag> Codec;\n\nclass Session : noncopyable\n{\n public:\n  explicit Session(const TcpConnectionPtr& conn)\n    : codec_(std::bind(&Session::onMessage, this, _1, _2, _3)),\n      file_(getFileName(conn))\n  {\n    conn->setMessageCallback(\n        std::bind(&Codec::onMessage, &codec_, _1, _2, _3));\n  }\n\n private:\n\n  // FIXME: duplicate code LogFile\n  // or use LogFile instead\n  string getFileName(const TcpConnectionPtr& conn)\n  {\n    string filename;\n    filename += conn->peerAddress().toIp();\n\n    char timebuf[32];\n    struct tm tm;\n    time_t now = time(NULL);\n    gmtime_r(&now, &tm); // FIXME: localtime_r ?\n    strftime(timebuf, sizeof timebuf, \".%Y%m%d-%H%M%S.\", &tm);\n    filename += timebuf;\n\n    char buf[32];\n    snprintf(buf, sizeof buf, \"%d\", globalCount_.incrementAndGet());\n    filename += buf;\n\n    filename += \".log\";\n    LOG_INFO << \"Session of \" << conn->name() << \" file \" << filename;\n    return filename;\n  }\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 const MessagePtr& message,\n                 Timestamp time)\n  {\n    LogRecord* logRecord = muduo::down_cast<LogRecord*>(message.get());\n    if (logRecord->has_heartbeat())\n    {\n      // FIXME ?\n    }\n    const char* sep = \"==========\\n\";\n    std::string str = logRecord->DebugString();\n    file_.append(str.c_str(), str.size());\n    file_.append(sep, strlen(sep));\n    LOG_DEBUG << str;\n  }\n\n  Codec codec_;\n  FileUtil::AppendFile file_;\n  static AtomicInt32 globalCount_;\n};\ntypedef std::shared_ptr<Session> SessionPtr;\n\nAtomicInt32 Session::globalCount_;\n\nclass LogServer : noncopyable\n{\n public:\n  LogServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)\n    : loop_(loop),\n      server_(loop_, listenAddr, \"AceLoggingServer\")\n  {\n    server_.setConnectionCallback(\n        std::bind(&LogServer::onConnection, this, _1));\n    if (numThreads > 1)\n    {\n      server_.setThreadNum(numThreads);\n    }\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      SessionPtr session(new Session(conn));\n      conn->setContext(session);\n    }\n    else\n    {\n      conn->setContext(SessionPtr());\n    }\n  }\n\n  EventLoop* loop_;\n  TcpServer server_;\n};\n\n}  // namespace logging\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  int port = argc > 1 ? atoi(argv[1]) : 50000;\n  LOG_INFO << \"Listen on port \" << port;\n  InetAddress listenAddr(static_cast<uint16_t>(port));\n  int numThreads = argc > 2 ? atoi(argv[2]) : 1;\n  logging::LogServer server(&loop, listenAddr, numThreads);\n  server.start();\n  loop.loop();\n\n}\n"
  },
  {
    "path": "examples/ace/ttcp/CMakeLists.txt",
    "content": "if(BOOSTPO_LIBRARY)\n  add_executable(ttcp_blocking ttcp_blocking.cc common.cc main.cc)\n  target_link_libraries(ttcp_blocking muduo_base boost_program_options)\n  set_target_properties(ttcp_blocking PROPERTIES COMPILE_FLAGS \"-Wno-error=old-style-cast -Wno-error=conversion\")\n\n  add_executable(ttcp_muduo ttcp.cc common.cc main.cc)\n  target_link_libraries(ttcp_muduo muduo_net boost_program_options)\n\n  if(BOOSTSYSTEM_LIBRARY)\n    add_executable(ttcp_asio_sync ttcp_asio_sync.cc common.cc main.cc)\n    target_link_libraries(ttcp_asio_sync muduo_base boost_program_options boost_system)\n\n    add_executable(ttcp_asio_async ttcp_asio_async.cc common.cc main.cc)\n    target_link_libraries(ttcp_asio_async muduo_base boost_program_options boost_system)\n  endif()\nendif()\n\n"
  },
  {
    "path": "examples/ace/ttcp/common.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n#include \"muduo/base/Types.h\"\n\n#include <boost/program_options.hpp>\n\n#include <iostream>\n\n#include <netdb.h>\n#include <stdio.h>\n\nnamespace po = boost::program_options;\n\nbool parseCommandLine(int argc, char* argv[], Options* opt)\n{\n  po::options_description desc(\"Allowed options\");\n  desc.add_options()\n      (\"help,h\", \"Help\")\n      (\"port,p\", po::value<uint16_t>(&opt->port)->default_value(5001), \"TCP port\")\n      (\"length,l\", po::value<int>(&opt->length)->default_value(65536), \"Buffer length\")\n      (\"number,n\", po::value<int>(&opt->number)->default_value(8192), \"Number of buffers\")\n      (\"trans,t\",  po::value<std::string>(&opt->host), \"Transmit\")\n      (\"recv,r\", \"Receive\")\n      (\"nodelay,D\", \"set TCP_NODELAY\")\n      ;\n\n  po::variables_map vm;\n  po::store(po::parse_command_line(argc, argv, desc), vm);\n  po::notify(vm);\n\n  opt->transmit = vm.count(\"trans\");\n  opt->receive = vm.count(\"recv\");\n  opt->nodelay = vm.count(\"nodelay\");\n  if (vm.count(\"help\"))\n  {\n    std::cout << desc << std::endl;\n    return false;\n  }\n\n  if (opt->transmit == opt->receive)\n  {\n    printf(\"either -t or -r must be specified.\\n\");\n    return false;\n  }\n\n  printf(\"port = %d\\n\", opt->port);\n  if (opt->transmit)\n  {\n    printf(\"buffer length = %d\\n\", opt->length);\n    printf(\"number of buffers = %d\\n\", opt->number);\n  }\n  else\n  {\n    printf(\"accepting...\\n\");\n  }\n  return true;\n}\n\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n\nstruct sockaddr_in resolveOrDie(const char* host, uint16_t port)\n{\n  struct hostent* he = ::gethostbyname(host);\n  if (!he)\n  {\n    perror(\"gethostbyname\");\n    exit(1);\n  }\n  assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));\n  struct sockaddr_in addr;\n  muduo::memZero(&addr, sizeof(addr));\n  addr.sin_family = AF_INET;\n  addr.sin_port = htons(port);\n  addr.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);\n  return addr;\n}\n\n"
  },
  {
    "path": "examples/ace/ttcp/common.h",
    "content": "#pragma once\n\n#include <string>\n#include <stdint.h>\n\nstruct Options\n{\n  uint16_t port;\n  int length;\n  int number;\n  bool transmit, receive, nodelay;\n  std::string host;\n  Options()\n    : port(0), length(0), number(0),\n      transmit(false), receive(false), nodelay(false)\n  {\n  }\n};\n\nbool parseCommandLine(int argc, char* argv[], Options* opt);\nstruct sockaddr_in resolveOrDie(const char* host, uint16_t port);\n\nstruct SessionMessage\n{\n  int32_t number;\n  int32_t length;\n} __attribute__ ((__packed__));\n\nstruct PayloadMessage\n{\n  int32_t length;\n  char data[0];\n};\n\nvoid transmit(const Options& opt);\n\nvoid receive(const Options& opt);\n"
  },
  {
    "path": "examples/ace/ttcp/main.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n\n#include <assert.h>\n\nint main(int argc, char* argv[])\n{\n  Options options;\n  if (parseCommandLine(argc, argv, &options))\n  {\n    if (options.transmit)\n    {\n      transmit(options);\n    }\n    else if (options.receive)\n    {\n      receive(options);\n    }\n    else\n    {\n      assert(0);\n    }\n  }\n}\n\n"
  },
  {
    "path": "examples/ace/ttcp/ttcp.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoop* g_loop;\n\nstruct Context\n{\n  int count;\n  int64_t bytes;\n  SessionMessage session;\n  Buffer output;\n\n  Context()\n    : count(0),\n      bytes(0)\n  {\n    session.number = 0;\n    session.length = 0;\n  }\n};\n\n/////////////////////////////////////////////////////////////////////\n// T R A N S M I T\n/////////////////////////////////////////////////////////////////////\n\nnamespace trans\n{\n\nvoid onConnection(const Options& opt, const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    printf(\"connected\\n\");\n    Context context;\n    context.count = 1;\n    context.bytes = opt.length;\n    context.session.number = opt.number;\n    context.session.length = opt.length;\n    context.output.appendInt32(opt.length);\n    context.output.ensureWritableBytes(opt.length);\n    for (int i = 0; i < opt.length; ++i)\n    {\n      context.output.beginWrite()[i] = \"0123456789ABCDEF\"[i % 16];\n    }\n    context.output.hasWritten(opt.length);\n    conn->setContext(context);\n\n    SessionMessage sessionMessage = { 0, 0 };\n    sessionMessage.number = htonl(opt.number);\n    sessionMessage.length = htonl(opt.length);\n    conn->send(&sessionMessage, sizeof(sessionMessage));\n\n    conn->send(context.output.toStringPiece());\n  }\n  else\n  {\n    const Context& context = boost::any_cast<Context>(conn->getContext());\n    LOG_INFO << \"payload bytes \" << context.bytes;\n    conn->getLoop()->quit();\n  }\n}\n\nvoid onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n{\n  Context* context = boost::any_cast<Context>(conn->getMutableContext());\n  while (buf->readableBytes() >= sizeof(int32_t))\n  {\n    int32_t length = buf->readInt32();\n    if (length == context->session.length)\n    {\n      if (context->count < context->session.number)\n      {\n        conn->send(context->output.toStringPiece());\n        ++context->count;\n        context->bytes += length;\n      }\n      else\n      {\n        conn->shutdown();\n        break;\n      }\n    }\n    else\n    {\n      conn->shutdown();\n      break;\n    }\n  }\n}\n\n}  // namespace trans\n\nvoid transmit(const Options& opt)\n{\n  InetAddress addr(opt.port);\n  if (!InetAddress::resolve(opt.host, &addr))\n  {\n    LOG_FATAL << \"Unable to resolve \" << opt.host;\n  }\n  muduo::Timestamp start(muduo::Timestamp::now());\n  EventLoop loop;\n  g_loop = &loop;\n  TcpClient client(&loop, addr, \"TtcpClient\");\n  client.setConnectionCallback(\n      std::bind(&trans::onConnection, opt, _1));\n  client.setMessageCallback(\n      std::bind(&trans::onMessage, _1, _2, _3));\n  client.connect();\n  loop.loop();\n  double elapsed = timeDifference(muduo::Timestamp::now(), start);\n  double total_mb = 1.0 * opt.length * opt.number / 1024 / 1024;\n  printf(\"%.3f MiB transferred\\n%.3f MiB/s\\n\", total_mb, total_mb / elapsed);\n}\n\n/////////////////////////////////////////////////////////////////////\n// R E C E I V E\n/////////////////////////////////////////////////////////////////////\n\nnamespace receiving\n{\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    Context context;\n    conn->setContext(context);\n  }\n  else\n  {\n    const Context& context = boost::any_cast<Context>(conn->getContext());\n    LOG_INFO << \"payload bytes \" << context.bytes;\n    conn->getLoop()->quit();\n  }\n}\n\nvoid onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n{\n  while (buf->readableBytes() >= sizeof(int32_t))\n  {\n    Context* context = boost::any_cast<Context>(conn->getMutableContext());\n    SessionMessage& session = context->session;\n    if (session.number == 0 && session.length == 0)\n    {\n      if (buf->readableBytes() >= sizeof(SessionMessage))\n      {\n        session.number = buf->readInt32();\n        session.length = buf->readInt32();\n        context->output.appendInt32(session.length);\n        printf(\"receive number = %d\\nreceive length = %d\\n\",\n               session.number, session.length);\n      }\n      else\n      {\n        break;\n      }\n    }\n    else\n    {\n      const unsigned total_len = session.length + static_cast<int>(sizeof(int32_t));\n      const int32_t length = buf->peekInt32();\n      if (length == session.length)\n      {\n        if (buf->readableBytes() >= total_len)\n        {\n          buf->retrieve(total_len);\n          conn->send(context->output.toStringPiece());\n          ++context->count;\n          context->bytes += length;\n          if (context->count >= session.number)\n          {\n            conn->shutdown();\n            break;\n          }\n        }\n        else\n        {\n          break;\n        }\n      }\n      else\n      {\n        printf(\"wrong length %d\\n\", length);\n        conn->shutdown();\n        break;\n      }\n    }\n  }\n}\n\n}  // namespace receiving\n\nvoid receive(const Options& opt)\n{\n  EventLoop loop;\n  g_loop = &loop;\n  InetAddress listenAddr(opt.port);\n  TcpServer server(&loop, listenAddr, \"TtcpReceive\");\n  server.setConnectionCallback(\n      std::bind(&receiving::onConnection, _1));\n  server.setMessageCallback(\n      std::bind(&receiving::onMessage, _1, _2, _3));\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/ace/ttcp/ttcp_asio_async.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n\n#include \"muduo/base/Logging.h\"\n#include <boost/asio.hpp>\n#include <stdio.h>\n\nusing boost::asio::ip::tcp;\n\nvoid transmit(const Options& opt)\n{\n  try\n  {\n  }\n  catch (std::exception& e)\n  {\n    LOG_ERROR << e.what();\n  }\n}\n\nclass TtcpServerConnection : public std::enable_shared_from_this<TtcpServerConnection>,\n                             muduo::noncopyable\n{\n public:\n#if BOOST_VERSION < 107000L\n  TtcpServerConnection(boost::asio::io_service& io_service)\n    : socket_(io_service), count_(0), payload_(NULL), ack_(0)\n#else\n  TtcpServerConnection(const tcp::socket::executor_type& executor)\n    : socket_(executor), count_(0), payload_(NULL), ack_(0)\n#endif\n  {\n    sessionMessage_.number = 0;\n    sessionMessage_.length = 0;\n  }\n\n  ~TtcpServerConnection()\n  {\n    ::free(payload_);\n  }\n\n  tcp::socket& socket() { return socket_; }\n\n  void start()\n  {\n    std::ostringstream oss;\n    oss << socket_.remote_endpoint();\n    LOG_INFO << \"Got connection from \" << oss.str();\n    doReadSession();\n  }\n\n private:\n  void doReadSession()\n  {\n    auto self(shared_from_this());\n    boost::asio::async_read(\n        socket_, boost::asio::buffer(&sessionMessage_, sizeof(sessionMessage_)),\n        [this, self](const boost::system::error_code& error, size_t len)\n        {\n          if (!error && len == sizeof sessionMessage_)\n          {\n            sessionMessage_.number = ntohl(sessionMessage_.number);\n            sessionMessage_.length = ntohl(sessionMessage_.length);\n            printf(\"receive number = %d\\nreceive length = %d\\n\",\n                   sessionMessage_.number, sessionMessage_.length);\n            const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage_.length);\n            payload_ = static_cast<PayloadMessage*>(::malloc(total_len));\n            doReadLength();\n          }\n          else\n          {\n            LOG_ERROR << \"read session message: \" << error.message();\n          }\n        });\n  }\n\n  void doReadLength()\n  {\n    auto self(shared_from_this());\n    payload_->length = 0;\n    boost::asio::async_read(\n        socket_, boost::asio::buffer(&payload_->length, sizeof payload_->length),\n        [this, self](const boost::system::error_code& error, size_t len)\n        {\n          if (!error && len == sizeof payload_->length)\n          {\n            payload_->length = ntohl(payload_->length);\n            doReadPayload();\n          }\n          else\n          {\n            LOG_ERROR << \"read length: \" << error.message();\n          }\n        });\n  }\n\n  void doReadPayload()\n  {\n    assert(payload_->length == sessionMessage_.length);\n    auto self(shared_from_this());\n    boost::asio::async_read(\n        socket_, boost::asio::buffer(&payload_->data, payload_->length),\n        [this, self](const boost::system::error_code& error, size_t len)\n        {\n          if (!error && len == static_cast<size_t>(payload_->length))\n          {\n            doWriteAck();\n          }\n          else\n          {\n            LOG_ERROR << \"read payload data: \" << error.message();\n          }\n        });\n  }\n\n  void doWriteAck()\n  {\n    auto self(shared_from_this());\n    ack_ = htonl(payload_->length);\n    boost::asio::async_write(\n        socket_, boost::asio::buffer(&ack_, sizeof ack_),\n        [this, self](const boost::system::error_code& error, size_t len)\n        {\n          if (!error && len == sizeof ack_)\n          {\n            if (++count_ < sessionMessage_.number)\n            {\n              doReadLength();\n            }\n            else\n            {\n              LOG_INFO << \"Done\";\n            }\n          }\n          else\n          {\n            LOG_ERROR << \"write ack: \" << error.message();\n          }\n        });\n  }\n\n  tcp::socket socket_;\n  int count_;\n  struct SessionMessage sessionMessage_;\n  struct PayloadMessage* payload_;\n  int32_t ack_;\n};\ntypedef std::shared_ptr<TtcpServerConnection> TtcpServerConnectionPtr;\n\nvoid doAccept(tcp::acceptor& acceptor)\n{\n#if BOOST_VERSION < 107000L\n  // no need to pre-create new_connection if we use asio 1.12 or boost 1.66+\n  TtcpServerConnectionPtr new_connection(new TtcpServerConnection(acceptor.get_io_service()));\n#else\n  TtcpServerConnectionPtr new_connection(new TtcpServerConnection(acceptor.get_executor()));\n#endif\n  acceptor.async_accept(\n      new_connection->socket(),\n      [&acceptor, new_connection](boost::system::error_code error)  // move new_connection in C++14\n      {\n        if (!error)\n        {\n          new_connection->start();\n        }\n        doAccept(acceptor);\n      });\n}\n\nvoid receive(const Options& opt)\n{\n  try\n  {\n    boost::asio::io_service io_service;\n    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), opt.port));\n    doAccept(acceptor);\n    io_service.run();\n  }\n  catch (std::exception& e)\n  {\n    LOG_ERROR << e.what();\n  }\n}\n\n"
  },
  {
    "path": "examples/ace/ttcp/ttcp_asio_sync.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n\n#include \"muduo/base/Logging.h\"\n#include <boost/asio.hpp>\n#include <stdio.h>\n\nusing boost::asio::ip::tcp;\n\nvoid transmit(const Options& opt)\n{\n  try\n  {\n  }\n  catch (std::exception& e)\n  {\n    LOG_ERROR << e.what();\n  }\n}\n\nvoid receive(const Options& opt)\n{\n  try\n  {\n    boost::asio::io_service io_service;\n    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), opt.port));\n    tcp::socket socket(io_service);\n    acceptor.accept(socket);\n\n    struct SessionMessage sessionMessage = { 0, 0 };\n    boost::system::error_code error;\n    size_t nr = boost::asio::read(socket, boost::asio::buffer(&sessionMessage, sizeof sessionMessage),\n#if BOOST_VERSION < 104700L\n                                  boost::asio::transfer_all(),\n#endif\n                                  error);\n    if (nr != sizeof sessionMessage)\n    {\n      LOG_ERROR << \"read session message: \" << error.message();\n      exit(1);\n    }\n\n    sessionMessage.number = ntohl(sessionMessage.number);\n    sessionMessage.length = ntohl(sessionMessage.length);\n    printf(\"receive number = %d\\nreceive length = %d\\n\",\n           sessionMessage.number, sessionMessage.length);\n    const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage.length);\n    PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));\n    std::unique_ptr<PayloadMessage, void (*)(void*)> freeIt(payload, ::free);\n    assert(payload);\n\n    for (int i = 0; i < sessionMessage.number; ++i)\n    {\n      payload->length = 0;\n      if (boost::asio::read(socket, boost::asio::buffer(&payload->length, sizeof(payload->length)),\n#if BOOST_VERSION < 104700L\n                            boost::asio::transfer_all(),\n#endif\n                            error) != sizeof(payload->length))\n      {\n        LOG_ERROR << \"read length: \" << error.message();\n        exit(1);\n      }\n      payload->length = ntohl(payload->length);\n      assert(payload->length == sessionMessage.length);\n      if (boost::asio::read(socket, boost::asio::buffer(payload->data, payload->length),\n#if BOOST_VERSION < 104700L\n                            boost::asio::transfer_all(),\n#endif\n                            error) != static_cast<size_t>(payload->length))\n      {\n        LOG_ERROR << \"read payload data: \" << error.message();\n        exit(1);\n      }\n      int32_t ack = htonl(payload->length);\n      if (boost::asio::write(socket, boost::asio::buffer(&ack, sizeof(ack))) != sizeof(ack))\n      {\n        LOG_ERROR << \"write ack: \" << error.message();\n        exit(1);\n      }\n    }\n  }\n  catch (std::exception& e)\n  {\n    LOG_ERROR << e.what();\n  }\n}\n"
  },
  {
    "path": "examples/ace/ttcp/ttcp_blocking.cc",
    "content": "#include \"examples/ace/ttcp/common.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/base/Types.h\"\n\n#undef NDEBUG\n\n#include <assert.h>\n#include <errno.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <netinet/in.h>\n#include <arpa/inet.h>\n\nstatic int acceptOrDie(uint16_t port)\n{\n  int listenfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n  assert(listenfd >= 0);\n\n  int yes = 1;\n  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))\n  {\n    perror(\"setsockopt\");\n    exit(1);\n  }\n\n  struct sockaddr_in addr;\n  muduo::memZero(&addr, sizeof(addr));\n  addr.sin_family = AF_INET;\n  addr.sin_port = htons(port);\n  addr.sin_addr.s_addr = INADDR_ANY;\n  if (bind(listenfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)))\n  {\n    perror(\"bind\");\n    exit(1);\n  }\n\n  if (listen(listenfd, 5))\n  {\n    perror(\"listen\");\n    exit(1);\n  }\n\n  struct sockaddr_in peer_addr;\n  muduo::memZero(&peer_addr, sizeof(peer_addr));\n  socklen_t addrlen = 0;\n  int sockfd = ::accept(listenfd, reinterpret_cast<struct sockaddr*>(&peer_addr), &addrlen);\n  if (sockfd < 0)\n  {\n    perror(\"accept\");\n    exit(1);\n  }\n  ::close(listenfd);\n  return sockfd;\n}\n\nstatic int write_n(int sockfd, const void* buf, int length)\n{\n  int written = 0;\n  while (written < length)\n  {\n    ssize_t nw = ::write(sockfd, static_cast<const char*>(buf) + written, length - written);\n    if (nw > 0)\n    {\n      written += static_cast<int>(nw);\n    }\n    else if (nw == 0)\n    {\n      break;  // EOF\n    }\n    else if (errno != EINTR)\n    {\n      perror(\"write\");\n      break;\n    }\n  }\n  return written;\n}\n\nstatic int read_n(int sockfd, void* buf, int length)\n{\n  int nread = 0;\n  while (nread < length)\n  {\n    ssize_t nr = ::read(sockfd, static_cast<char*>(buf) + nread, length - nread);\n    if (nr > 0)\n    {\n      nread += static_cast<int>(nr);\n    }\n    else if (nr == 0)\n    {\n      break;  // EOF\n    }\n    else if (errno != EINTR)\n    {\n      perror(\"read\");\n      break;\n    }\n  }\n  return nread;\n}\n\nvoid transmit(const Options& opt)\n{\n  struct sockaddr_in addr = resolveOrDie(opt.host.c_str(), opt.port);\n  printf(\"connecting to %s:%d\\n\", inet_ntoa(addr.sin_addr), opt.port);\n\n  int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n  assert(sockfd >= 0);\n  int ret = ::connect(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));\n  if (ret)\n  {\n    perror(\"connect\");\n    printf(\"Unable to connect %s\\n\", opt.host.c_str());\n    ::close(sockfd);\n    return;\n  }\n\n  printf(\"connected\\n\");\n  muduo::Timestamp start(muduo::Timestamp::now());\n  struct SessionMessage sessionMessage = { 0, 0 };\n  sessionMessage.number = htonl(opt.number);\n  sessionMessage.length = htonl(opt.length);\n  if (write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))\n  {\n    perror(\"write SessionMessage\");\n    exit(1);\n  }\n\n  const int total_len = static_cast<int>(sizeof(int32_t) + opt.length);\n  PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));\n  assert(payload);\n  payload->length = htonl(opt.length);\n  for (int i = 0; i < opt.length; ++i)\n  {\n    payload->data[i] = \"0123456789ABCDEF\"[i % 16];\n  }\n\n  double total_mb = 1.0 * opt.length * opt.number / 1024 / 1024;\n  printf(\"%.3f MiB in total\\n\", total_mb);\n\n  for (int i = 0; i < opt.number; ++i)\n  {\n    int nw = write_n(sockfd, payload, total_len);\n    assert(nw == total_len);\n\n    int ack = 0;\n    int nr = read_n(sockfd, &ack, sizeof(ack));\n    assert(nr == sizeof(ack));\n    ack = ntohl(ack);\n    assert(ack == opt.length);\n  }\n\n  ::free(payload);\n  ::close(sockfd);\n  double elapsed = timeDifference(muduo::Timestamp::now(), start);\n  printf(\"%.3f seconds\\n%.3f MiB/s\\n\", elapsed, total_mb / elapsed);\n}\n\nvoid receive(const Options& opt)\n{\n  int sockfd = acceptOrDie(opt.port);\n\n  struct SessionMessage sessionMessage = { 0, 0 };\n  if (read_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))\n  {\n    perror(\"read SessionMessage\");\n    exit(1);\n  }\n\n  sessionMessage.number = ntohl(sessionMessage.number);\n  sessionMessage.length = ntohl(sessionMessage.length);\n  printf(\"receive number = %d\\nreceive length = %d\\n\",\n         sessionMessage.number, sessionMessage.length);\n  const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage.length);\n  PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));\n  assert(payload);\n\n  for (int i = 0; i < sessionMessage.number; ++i)\n  {\n    payload->length = 0;\n    if (read_n(sockfd, &payload->length, sizeof(payload->length)) != sizeof(payload->length))\n    {\n      perror(\"read length\");\n      exit(1);\n    }\n    payload->length = ntohl(payload->length);\n    assert(payload->length == sessionMessage.length);\n    if (read_n(sockfd, payload->data, payload->length) != payload->length)\n    {\n      perror(\"read payload data\");\n      exit(1);\n    }\n    int32_t ack = htonl(payload->length);\n    if (write_n(sockfd, &ack, sizeof(ack)) != sizeof(ack))\n    {\n      perror(\"write ack\");\n      exit(1);\n    }\n  }\n  ::free(payload);\n  ::close(sockfd);\n}\n\n"
  },
  {
    "path": "examples/asio/chat/CMakeLists.txt",
    "content": "add_executable(asio_chat_client client.cc)\ntarget_link_libraries(asio_chat_client muduo_net)\n\nadd_executable(asio_chat_loadtest loadtest.cc)\ntarget_link_libraries(asio_chat_loadtest muduo_net)\n\nadd_executable(asio_chat_server server.cc)\ntarget_link_libraries(asio_chat_server muduo_net)\n\nadd_executable(asio_chat_server_threaded server_threaded.cc)\ntarget_link_libraries(asio_chat_server_threaded muduo_net)\n\nadd_executable(asio_chat_server_threaded_efficient server_threaded_efficient.cc)\ntarget_link_libraries(asio_chat_server_threaded_efficient muduo_net)\n\nadd_executable(asio_chat_server_threaded_highperformance server_threaded_highperformance.cc)\ntarget_link_libraries(asio_chat_server_threaded_highperformance muduo_net)\n\n"
  },
  {
    "path": "examples/asio/chat/client.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <iostream>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChatClient : noncopyable\n{\n public:\n  ChatClient(EventLoop* loop, const InetAddress& serverAddr)\n    : client_(loop, serverAddr, \"ChatClient\"),\n      codec_(std::bind(&ChatClient::onStringMessage, this, _1, _2, _3))\n  {\n    client_.setConnectionCallback(\n        std::bind(&ChatClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n    client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void disconnect()\n  {\n    client_.disconnect();\n  }\n\n  void write(const StringPiece& message)\n  {\n    MutexLockGuard lock(mutex_);\n    if (connection_)\n    {\n      codec_.send(get_pointer(connection_), message);\n    }\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    MutexLockGuard lock(mutex_);\n    if (conn->connected())\n    {\n      connection_ = conn;\n    }\n    else\n    {\n      connection_.reset();\n    }\n  }\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    printf(\"<<< %s\\n\", message.c_str());\n  }\n\n  TcpClient client_;\n  LengthHeaderCodec codec_;\n  MutexLock mutex_;\n  TcpConnectionPtr connection_ GUARDED_BY(mutex_);\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 2)\n  {\n    EventLoopThread loopThread;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(argv[1], port);\n\n    ChatClient client(loopThread.startLoop(), serverAddr);\n    client.connect();\n    std::string line;\n    while (std::getline(std::cin, line))\n    {\n      client.write(line);\n    }\n    client.disconnect();\n    CurrentThread::sleepUsec(1000*1000);  // wait for disconnect, see ace/logging/client.cc\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip port\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/asio/chat/codec.h",
    "content": "#ifndef MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H\n#define MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Buffer.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/TcpConnection.h\"\n\nclass LengthHeaderCodec : muduo::noncopyable\n{\n public:\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                const muduo::string& message,\n                                muduo::Timestamp)> StringMessageCallback;\n\n  explicit LengthHeaderCodec(const StringMessageCallback& cb)\n    : messageCallback_(cb)\n  {\n  }\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp receiveTime)\n  {\n    while (buf->readableBytes() >= kHeaderLen) // kHeaderLen == 4\n    {\n      // FIXME: use Buffer::peekInt32()\n      const void* data = buf->peek();\n      int32_t be32 = *static_cast<const int32_t*>(data); // SIGBUS\n      const int32_t len = muduo::net::sockets::networkToHost32(be32);\n      if (len > 65536 || len < 0)\n      {\n        LOG_ERROR << \"Invalid length \" << len;\n        conn->shutdown();  // FIXME: disable reading\n        break;\n      }\n      else if (buf->readableBytes() >= len + kHeaderLen)\n      {\n        buf->retrieve(kHeaderLen);\n        muduo::string message(buf->peek(), len);\n        messageCallback_(conn, message, receiveTime);\n        buf->retrieve(len);\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  // FIXME: TcpConnectionPtr\n  void send(muduo::net::TcpConnection* conn,\n            const muduo::StringPiece& message)\n  {\n    muduo::net::Buffer buf;\n    buf.append(message.data(), message.size());\n    int32_t len = static_cast<int32_t>(message.size());\n    int32_t be32 = muduo::net::sockets::hostToNetwork32(len);\n    buf.prepend(&be32, sizeof be32);\n    conn->send(&buf);\n  }\n\n private:\n  StringMessageCallback messageCallback_;\n  const static size_t kHeaderLen = sizeof(int32_t);\n};\n\n#endif  // MUDUO_EXAMPLES_ASIO_CHAT_CODEC_H\n"
  },
  {
    "path": "examples/asio/chat/loadtest.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint g_connections = 0;\nAtomicInt32 g_aliveConnections;\nAtomicInt32 g_messagesReceived;\nTimestamp g_startTime;\nstd::vector<Timestamp> g_receiveTime;\nEventLoop* g_loop;\nstd::function<void()> g_statistic;\n\nclass ChatClient : noncopyable\n{\n public:\n  ChatClient(EventLoop* loop, const InetAddress& serverAddr)\n    : loop_(loop),\n      client_(loop, serverAddr, \"LoadTestClient\"),\n      codec_(std::bind(&ChatClient::onStringMessage, this, _1, _2, _3))\n  {\n    client_.setConnectionCallback(\n        std::bind(&ChatClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n    //client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void disconnect()\n  {\n    // client_.disconnect();\n  }\n\n  Timestamp receiveTime() const { return receiveTime_; }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      connection_ = conn;\n      if (g_aliveConnections.incrementAndGet() == g_connections)\n      {\n        LOG_INFO << \"all connected\";\n        loop_->runAfter(10.0, std::bind(&ChatClient::send, this));\n      }\n    }\n    else\n    {\n      connection_.reset();\n    }\n  }\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    // printf(\"<<< %s\\n\", message.c_str());\n    receiveTime_ = loop_->pollReturnTime();\n    int received = g_messagesReceived.incrementAndGet();\n    if (received == g_connections)\n    {\n      Timestamp endTime = Timestamp::now();\n      LOG_INFO << \"all received \" << g_connections << \" in \"\n               << timeDifference(endTime, g_startTime);\n      g_loop->queueInLoop(g_statistic);\n    }\n    else if (received % 1000 == 0)\n    {\n      LOG_DEBUG << received;\n    }\n  }\n\n  void send()\n  {\n    g_startTime = Timestamp::now();\n    codec_.send(get_pointer(connection_), \"hello\");\n    LOG_DEBUG << \"sent\";\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  LengthHeaderCodec codec_;\n  TcpConnectionPtr connection_;\n  Timestamp receiveTime_;\n};\n\nvoid statistic(const std::vector<std::unique_ptr<ChatClient>>& clients)\n{\n  LOG_INFO << \"statistic \" << clients.size();\n  std::vector<double> seconds(clients.size());\n  for (size_t i = 0; i < clients.size(); ++i)\n  {\n    seconds[i] = timeDifference(clients[i]->receiveTime(), g_startTime);\n  }\n\n  std::sort(seconds.begin(), seconds.end());\n  for (size_t i = 0; i < clients.size(); i += std::max(static_cast<size_t>(1), clients.size()/20))\n  {\n    printf(\"%6zd%% %.6f\\n\", i*100/clients.size(), seconds[i]);\n  }\n  if (clients.size() >= 100)\n  {\n    printf(\"%6d%% %.6f\\n\", 99, seconds[clients.size() - clients.size()/100]);\n  }\n  printf(\"%6d%% %.6f\\n\", 100, seconds.back());\n}\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 3)\n  {\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(argv[1], port);\n    g_connections = atoi(argv[3]);\n    int threads = 0;\n    if (argc > 4)\n    {\n      threads = atoi(argv[4]);\n    }\n\n    EventLoop loop;\n    g_loop = &loop;\n    EventLoopThreadPool loopPool(&loop, \"chat-loadtest\");\n    loopPool.setThreadNum(threads);\n    loopPool.start();\n\n    g_receiveTime.reserve(g_connections);\n    std::vector<std::unique_ptr<ChatClient>> clients(g_connections);\n    g_statistic = std::bind(statistic, std::ref(clients));\n\n    for (int i = 0; i < g_connections; ++i)\n    {\n      clients[i].reset(new ChatClient(loopPool.getNextLoop(), serverAddr));\n      clients[i]->connect();\n      usleep(200);\n    }\n\n    loop.loop();\n    // client.disconnect();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip port connections [threads]\\n\", argv[0]);\n  }\n}\n\n\n"
  },
  {
    "path": "examples/asio/chat/server.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <set>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChatServer : noncopyable\n{\n public:\n  ChatServer(EventLoop* loop,\n             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"ChatServer\"),\n    codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3))\n  {\n    server_.setConnectionCallback(\n        std::bind(&ChatServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->peerAddress().toIpPort() << \" -> \"\n             << conn->localAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      connections_.insert(conn);\n    }\n    else\n    {\n      connections_.erase(conn);\n    }\n  }\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    for (ConnectionList::iterator it = connections_.begin();\n        it != connections_.end();\n        ++it)\n    {\n      codec_.send(get_pointer(*it), message);\n    }\n  }\n\n  typedef std::set<TcpConnectionPtr> ConnectionList;\n  TcpServer server_;\n  LengthHeaderCodec codec_;\n  ConnectionList connections_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress serverAddr(port);\n    ChatServer server(&loop, serverAddr);\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s port\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/asio/chat/server_threaded.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <set>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChatServer : noncopyable\n{\n public:\n  ChatServer(EventLoop* loop,\n             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"ChatServer\"),\n    codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3))\n  {\n    server_.setConnectionCallback(\n        std::bind(&ChatServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    MutexLockGuard lock(mutex_);\n    if (conn->connected())\n    {\n      connections_.insert(conn);\n    }\n    else\n    {\n      connections_.erase(conn);\n    }\n  }\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    MutexLockGuard lock(mutex_);\n    for (ConnectionList::iterator it = connections_.begin();\n        it != connections_.end();\n        ++it)\n    {\n      codec_.send(get_pointer(*it), message);\n    }\n  }\n\n  typedef std::set<TcpConnectionPtr> ConnectionList;\n  TcpServer server_;\n  LengthHeaderCodec codec_;\n  MutexLock mutex_;\n  ConnectionList connections_ GUARDED_BY(mutex_);\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress serverAddr(port);\n    ChatServer server(&loop, serverAddr);\n    if (argc > 2)\n    {\n      server.setThreadNum(atoi(argv[2]));\n    }\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s port [thread_num]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/asio/chat/server_threaded_efficient.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <set>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChatServer : noncopyable\n{\n public:\n  ChatServer(EventLoop* loop,\n             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"ChatServer\"),\n    codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3)),\n    connections_(new ConnectionList)\n  {\n    server_.setConnectionCallback(\n        std::bind(&ChatServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    MutexLockGuard lock(mutex_);\n    if (!connections_.unique())\n    {\n      connections_.reset(new ConnectionList(*connections_));\n    }\n    assert(connections_.unique());\n\n    if (conn->connected())\n    {\n      connections_->insert(conn);\n    }\n    else\n    {\n      connections_->erase(conn);\n    }\n  }\n\n  typedef std::set<TcpConnectionPtr> ConnectionList;\n  typedef std::shared_ptr<ConnectionList> ConnectionListPtr;\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    ConnectionListPtr connections = getConnectionList();\n    for (ConnectionList::iterator it = connections->begin();\n        it != connections->end();\n        ++it)\n    {\n      codec_.send(get_pointer(*it), message);\n    }\n  }\n\n  ConnectionListPtr getConnectionList()\n  {\n    MutexLockGuard lock(mutex_);\n    return connections_;\n  }\n\n  TcpServer server_;\n  LengthHeaderCodec codec_;\n  MutexLock mutex_;\n  ConnectionListPtr connections_ GUARDED_BY(mutex_);\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress serverAddr(port);\n    ChatServer server(&loop, serverAddr);\n    if (argc > 2)\n    {\n      server.setThreadNum(atoi(argv[2]));\n    }\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s port [thread_num]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/asio/chat/server_threaded_highperformance.cc",
    "content": "#include \"examples/asio/chat/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/ThreadLocalSingleton.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <set>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChatServer : noncopyable\n{\n public:\n  ChatServer(EventLoop* loop,\n             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"ChatServer\"),\n    codec_(std::bind(&ChatServer::onStringMessage, this, _1, _2, _3))\n  {\n    server_.setConnectionCallback(\n        std::bind(&ChatServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.setThreadInitCallback(std::bind(&ChatServer::threadInit, this, _1));\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->peerAddress().toIpPort() << \" -> \"\n             << conn->localAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      LocalConnections::instance().insert(conn);\n    }\n    else\n    {\n      LocalConnections::instance().erase(conn);\n    }\n  }\n\n  void onStringMessage(const TcpConnectionPtr&,\n                       const string& message,\n                       Timestamp)\n  {\n    EventLoop::Functor f = std::bind(&ChatServer::distributeMessage, this, message);\n    LOG_DEBUG;\n\n    MutexLockGuard lock(mutex_);\n    for (std::set<EventLoop*>::iterator it = loops_.begin();\n        it != loops_.end();\n        ++it)\n    {\n      (*it)->queueInLoop(f);\n    }\n    LOG_DEBUG;\n  }\n\n  typedef std::set<TcpConnectionPtr> ConnectionList;\n\n  void distributeMessage(const string& message)\n  {\n    LOG_DEBUG << \"begin\";\n    for (ConnectionList::iterator it = LocalConnections::instance().begin();\n        it != LocalConnections::instance().end();\n        ++it)\n    {\n      codec_.send(get_pointer(*it), message);\n    }\n    LOG_DEBUG << \"end\";\n  }\n\n  void threadInit(EventLoop* loop)\n  {\n    assert(LocalConnections::pointer() == NULL);\n    LocalConnections::instance();\n    assert(LocalConnections::pointer() != NULL);\n    MutexLockGuard lock(mutex_);\n    loops_.insert(loop);\n  }\n\n  TcpServer server_;\n  LengthHeaderCodec codec_;\n  typedef ThreadLocalSingleton<ConnectionList> LocalConnections;\n\n  MutexLock mutex_;\n  std::set<EventLoop*> loops_ GUARDED_BY(mutex_);\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress serverAddr(port);\n    ChatServer server(&loop, serverAddr);\n    if (argc > 2)\n    {\n      server.setThreadNum(atoi(argv[2]));\n    }\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s port [thread_num]\\n\", argv[0]);\n  }\n}\n\n\n"
  },
  {
    "path": "examples/asio/echo_see_simple",
    "content": ""
  },
  {
    "path": "examples/asio/tutorial/CMakeLists.txt",
    "content": "add_executable(asio_tutorial_timer2 timer2/timer.cc)\ntarget_link_libraries(asio_tutorial_timer2 muduo_net)\n\nadd_executable(asio_tutorial_timer3 timer3/timer.cc)\ntarget_link_libraries(asio_tutorial_timer3 muduo_net)\n\nadd_executable(asio_tutorial_timer4 timer4/timer.cc)\ntarget_link_libraries(asio_tutorial_timer4 muduo_net)\n\nadd_executable(asio_tutorial_timer5 timer5/timer.cc)\ntarget_link_libraries(asio_tutorial_timer5 muduo_net)\n\nadd_executable(asio_tutorial_timer6 timer6/timer.cc)\ntarget_link_libraries(asio_tutorial_timer6 muduo_net)\n\n"
  },
  {
    "path": "examples/asio/tutorial/daytime_see_simple",
    "content": ""
  },
  {
    "path": "examples/asio/tutorial/there_is_no_timer1",
    "content": ""
  },
  {
    "path": "examples/asio/tutorial/timer2/timer.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n\n#include <iostream>\n\nvoid print()\n{\n  std::cout << \"Hello, world!\\n\";\n}\n\nint main()\n{\n  muduo::net::EventLoop loop;\n  loop.runAfter(5, print);\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/asio/tutorial/timer3/timer.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n\n#include <iostream>\n\nvoid print(muduo::net::EventLoop* loop, int* count)\n{\n  if (*count < 5)\n  {\n    std::cout << *count << \"\\n\";\n    ++(*count);\n\n    loop->runAfter(1, std::bind(print, loop, count));\n  }\n  else\n  {\n    loop->quit();\n  }\n}\n\nint main()\n{\n  muduo::net::EventLoop loop;\n  int count = 0;\n  // Note: loop.runEvery() is better for this use case.\n  loop.runAfter(1, std::bind(print, &loop, &count));\n  loop.loop();\n  std::cout << \"Final count is \" << count << \"\\n\";\n}\n\n"
  },
  {
    "path": "examples/asio/tutorial/timer4/timer.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n\n#include <iostream>\n\nclass Printer : muduo::noncopyable\n{\n public:\n  Printer(muduo::net::EventLoop* loop)\n    : loop_(loop),\n      count_(0)\n  {\n    // Note: loop.runEvery() is better for this use case.\n    loop_->runAfter(1, std::bind(&Printer::print, this));\n  }\n\n  ~Printer()\n  {\n    std::cout << \"Final count is \" << count_ << \"\\n\";\n  }\n\n  void print()\n  {\n    if (count_ < 5)\n    {\n      std::cout << count_ << \"\\n\";\n      ++count_;\n\n      loop_->runAfter(1, std::bind(&Printer::print, this));\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\nprivate:\n  muduo::net::EventLoop* loop_;\n  int count_;\n};\n\nint main()\n{\n  muduo::net::EventLoop loop;\n  Printer printer(&loop);\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/asio/tutorial/timer5/timer.cc",
    "content": "#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n\n#include <iostream>\n\nclass Printer : muduo::noncopyable\n{\n public:\n  Printer(muduo::net::EventLoop* loop1, muduo::net::EventLoop* loop2)\n    : loop1_(loop1),\n      loop2_(loop2),\n      count_(0)\n  {\n    loop1_->runAfter(1, std::bind(&Printer::print1, this));\n    loop2_->runAfter(1, std::bind(&Printer::print2, this));\n  }\n\n  ~Printer()\n  {\n    std::cout << \"Final count is \" << count_ << \"\\n\";\n  }\n\n  void print1()\n  {\n    muduo::MutexLockGuard lock(mutex_);\n    if (count_ < 10)\n    {\n      std::cout << \"Timer 1: \" << count_ << \"\\n\";\n      ++count_;\n\n      loop1_->runAfter(1, std::bind(&Printer::print1, this));\n    }\n    else\n    {\n      loop1_->quit();\n    }\n  }\n\n  void print2()\n  {\n    muduo::MutexLockGuard lock(mutex_);\n    if (count_ < 10)\n    {\n      std::cout << \"Timer 2: \" << count_ << \"\\n\";\n      ++count_;\n\n      loop2_->runAfter(1, std::bind(&Printer::print2, this));\n    }\n    else\n    {\n      loop2_->quit();\n    }\n  }\n\nprivate:\n\n  muduo::MutexLock mutex_;\n  muduo::net::EventLoop* loop1_ PT_GUARDED_BY(mutex_);\n  muduo::net::EventLoop* loop2_ PT_GUARDED_BY(mutex_);\n  int count_ GUARDED_BY(mutex_);\n};\n\nint main()\n{\n  std::unique_ptr<Printer> printer;  // make sure printer lives longer than loops, to avoid\n                                     // race condition of calling print2() on destructed object.\n  muduo::net::EventLoop loop;\n  muduo::net::EventLoopThread loopThread;\n  muduo::net::EventLoop* loopInAnotherThread = loopThread.startLoop();\n  printer.reset(new Printer(&loop, loopInAnotherThread));\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/asio/tutorial/timer6/timer.cc",
    "content": "#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n\n#include <stdio.h>\n\n//\n// Minimize locking\n//\n\nclass Printer : muduo::noncopyable\n{\n public:\n  Printer(muduo::net::EventLoop* loop1, muduo::net::EventLoop* loop2)\n    : loop1_(loop1),\n      loop2_(loop2),\n      count_(0)\n  {\n    loop1_->runAfter(1, std::bind(&Printer::print1, this));\n    loop2_->runAfter(1, std::bind(&Printer::print2, this));\n  }\n\n  ~Printer()\n  {\n    // cout is not thread safe\n    //std::cout << \"Final count is \" << count_ << \"\\n\";\n    printf(\"Final count is %d\\n\", count_);\n  }\n\n  void print1()\n  {\n    bool shouldQuit = false;\n    int count = 0;\n\n    {\n      muduo::MutexLockGuard lock(mutex_);\n      if (count_ < 10)\n      {\n        count = count_;\n        ++count_;\n      }\n      else\n      {\n        shouldQuit = true;\n      }\n    }\n\n    // out of lock\n    if (shouldQuit)\n    {\n      // printf(\"loop1_->quit()\\n\");\n      loop1_->quit();\n    }\n    else\n    {\n      // cout is not thread safe\n      //std::cout << \"Timer 1: \" << count << \"\\n\";\n      printf(\"Timer 1: %d\\n\", count);\n      loop1_->runAfter(1, std::bind(&Printer::print1, this));\n    }\n  }\n\n  void print2()\n  {\n    bool shouldQuit = false;\n    int count = 0;\n\n    {\n      muduo::MutexLockGuard lock(mutex_);\n      if (count_ < 10)\n      {\n        count = count_;\n        ++count_;\n      }\n      else\n      {\n        shouldQuit = true;\n      }\n    }\n\n    // out of lock\n    if (shouldQuit)\n    {\n      // printf(\"loop2_->quit()\\n\");\n      loop2_->quit();\n    }\n    else\n    {\n      // cout is not thread safe\n      //std::cout << \"Timer 2: \" << count << \"\\n\";\n      printf(\"Timer 2: %d\\n\", count);\n      loop2_->runAfter(1, std::bind(&Printer::print2, this));\n    }\n  }\n\nprivate:\n\n  muduo::MutexLock mutex_;\n  muduo::net::EventLoop* loop1_;\n  muduo::net::EventLoop* loop2_;\n  int count_ GUARDED_BY(mutex_);\n};\n\nint main()\n{\n  std::unique_ptr<Printer> printer;  // make sure printer lives longer than loops, to avoid\n                                     // race condition of calling print2() on destructed object.\n  muduo::net::EventLoop loop;\n  muduo::net::EventLoopThread loopThread;\n  muduo::net::EventLoop* loopInAnotherThread = loopThread.startLoop();\n  printer.reset(new Printer(&loop, loopInAnotherThread));\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/cdns/CMakeLists.txt",
    "content": "add_library(muduo_cdns Resolver.cc)\ntarget_link_libraries(muduo_cdns muduo_net)\ntarget_link_libraries(muduo_cdns cares)\n\ninstall(TARGETS muduo_cdns DESTINATION lib)\ninstall(FILES Resolver.h DESTINATION include/muduo/cdns)\n\nadd_executable(cdns dns.cc)\ntarget_link_libraries(cdns muduo_cdns)\n\n"
  },
  {
    "path": "examples/cdns/Resolver.cc",
    "content": "#include \"examples/cdns/Resolver.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <ares.h>\n#include <netdb.h>\n#include <arpa/inet.h>  // inet_ntop\n#include <netinet/in.h>\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace cdns;\n\nnamespace\n{\ndouble getSeconds(struct timeval* tv)\n{\n  if (tv)\n    return double(tv->tv_sec) + double(tv->tv_usec)/1000000.0;\n  else\n    return -1.0;\n}\n\nconst char* getSocketType(int type)\n{\n  if (type == SOCK_DGRAM)\n    return \"UDP\";\n  else if (type == SOCK_STREAM)\n    return \"TCP\";\n  else\n    return \"Unknown\";\n}\n\nconst bool kDebug = false;\n}  // namespace\n\nResolver::Resolver(EventLoop* loop, Option opt)\n  : loop_(loop),\n    ctx_(NULL),\n    timerActive_(false)\n{\n  static char lookups[] = \"b\";\n  struct ares_options options;\n  int optmask = ARES_OPT_FLAGS;\n  options.flags = ARES_FLAG_NOCHECKRESP;\n  options.flags |= ARES_FLAG_STAYOPEN;\n  options.flags |= ARES_FLAG_IGNTC; // UDP only\n  optmask |= ARES_OPT_SOCK_STATE_CB;\n  options.sock_state_cb = &Resolver::ares_sock_state_callback;\n  options.sock_state_cb_data = this;\n  optmask |= ARES_OPT_TIMEOUT;\n  options.timeout = 2;\n  if (opt == kDNSonly)\n  {\n    optmask |= ARES_OPT_LOOKUPS;\n    options.lookups = lookups;\n  }\n\n  int status = ares_init_options(&ctx_, &options, optmask);\n  if (status != ARES_SUCCESS)\n  {\n    assert(0);\n  }\n  ares_set_socket_callback(ctx_, &Resolver::ares_sock_create_callback, this);\n}\n\nResolver::~Resolver()\n{\n  ares_destroy(ctx_);\n}\n\nbool Resolver::resolve(StringArg hostname, const Callback& cb)\n{\n  loop_->assertInLoopThread();\n  QueryData* queryData = new QueryData(this, cb);\n  ares_gethostbyname(ctx_, hostname.c_str(), AF_INET,\n      &Resolver::ares_host_callback, queryData);\n  struct timeval tv;\n  struct timeval* tvp = ares_timeout(ctx_, NULL, &tv);\n  double timeout = getSeconds(tvp);\n  LOG_DEBUG << \"timeout \" <<  timeout << \" active \" << timerActive_;\n  if (!timerActive_)\n  {\n    loop_->runAfter(timeout, std::bind(&Resolver::onTimer, this));\n    timerActive_ = true;\n  }\n  return queryData != NULL;\n}\n\nvoid Resolver::onRead(int sockfd, Timestamp t)\n{\n  LOG_DEBUG << \"onRead \" << sockfd << \" at \" << t.toString();\n  ares_process_fd(ctx_, sockfd, ARES_SOCKET_BAD);\n}\n\nvoid Resolver::onTimer()\n{\n  assert(timerActive_ == true);\n  ares_process_fd(ctx_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);\n  struct timeval tv;\n  struct timeval* tvp = ares_timeout(ctx_, NULL, &tv);\n  double timeout = getSeconds(tvp);\n  LOG_DEBUG << loop_->pollReturnTime().toString() << \" next timeout \" <<  timeout;\n\n  if (timeout < 0)\n  {\n    timerActive_ = false;\n  }\n  else\n  {\n    loop_->runAfter(timeout, std::bind(&Resolver::onTimer, this));\n  }\n}\n\nvoid Resolver::onQueryResult(int status, struct hostent* result, const Callback& callback)\n{\n  LOG_DEBUG << \"onQueryResult \" << status;\n  struct sockaddr_in addr;\n  memZero(&addr, sizeof addr);\n  addr.sin_family = AF_INET;\n  addr.sin_port = 0;\n  if (result)\n  {\n    addr.sin_addr = *reinterpret_cast<in_addr*>(result->h_addr);\n    if (kDebug)\n    {\n      printf(\"h_name %s\\n\", result->h_name);\n      for (char** alias = result->h_aliases; *alias != NULL; ++alias)\n      {\n        printf(\"alias: %s\\n\", *alias);\n      }\n      // printf(\"ttl %d\\n\", ttl);\n      // printf(\"h_length %d\\n\", result->h_length);\n      for (char** haddr = result->h_addr_list; *haddr != NULL; ++haddr)\n      {\n        char buf[32];\n        inet_ntop(AF_INET, *haddr, buf, sizeof buf);\n        printf(\"  %s\\n\", buf);\n      }\n    }\n  }\n  InetAddress inet(addr);\n  callback(inet);\n}\n\nvoid Resolver::onSockCreate(int sockfd, int type)\n{\n  loop_->assertInLoopThread();\n  assert(channels_.find(sockfd) == channels_.end());\n  Channel* channel = new Channel(loop_, sockfd);\n  channel->setReadCallback(std::bind(&Resolver::onRead, this, sockfd, _1));\n  channel->enableReading();\n  channels_[sockfd].reset(channel);\n}\n\nvoid Resolver::onSockStateChange(int sockfd, bool read, bool write)\n{\n  loop_->assertInLoopThread();\n  ChannelList::iterator it = channels_.find(sockfd);\n  assert(it != channels_.end());\n  if (read)\n  {\n    // update\n    // if (write) { } else { }\n  }\n  else\n  {\n    // remove\n    it->second->disableAll();\n    it->second->remove();\n    channels_.erase(it);\n  }\n}\n\nvoid Resolver::ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent)\n{\n  QueryData* query = static_cast<QueryData*>(data);\n\n  query->owner->onQueryResult(status, hostent, query->callback);\n  delete query;\n}\n\nint Resolver::ares_sock_create_callback(int sockfd, int type, void* data)\n{\n  LOG_TRACE << \"sockfd=\" << sockfd << \" type=\" << getSocketType(type);\n  static_cast<Resolver*>(data)->onSockCreate(sockfd, type);\n  return 0;\n}\n\nvoid Resolver::ares_sock_state_callback(void* data, int sockfd, int read, int write)\n{\n  LOG_TRACE << \"sockfd=\" << sockfd << \" read=\" << read << \" write=\" << write;\n  static_cast<Resolver*>(data)->onSockStateChange(sockfd, read, write);\n}\n\n"
  },
  {
    "path": "examples/cdns/Resolver.h",
    "content": "#ifndef MUDUO_EXAMPLES_CDNS_RESOLVER_H\n#define MUDUO_EXAMPLES_CDNS_RESOLVER_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <functional>\n#include <map>\n#include <memory>\n\nextern \"C\"\n{\n  struct hostent;\n  struct ares_channeldata;\n  typedef struct ares_channeldata* ares_channel;\n}\n\nnamespace muduo\n{\nnamespace net\n{\nclass Channel;\nclass EventLoop;\n}\n}\n\nnamespace cdns\n{\n\nclass Resolver : muduo::noncopyable\n{\n public:\n  typedef std::function<void(const muduo::net::InetAddress&)> Callback;\n  enum Option\n  {\n    kDNSandHostsFile,\n    kDNSonly,\n  };\n\n  explicit Resolver(muduo::net::EventLoop* loop, Option opt = kDNSandHostsFile);\n  ~Resolver();\n\n  bool resolve(muduo::StringArg hostname, const Callback& cb);\n\n private:\n\n  struct QueryData\n  {\n    Resolver* owner;\n    Callback callback;\n    QueryData(Resolver* o, const Callback& cb)\n      : owner(o), callback(cb)\n    {\n    }\n  };\n\n  muduo::net::EventLoop* loop_;\n  ares_channel ctx_;\n  bool timerActive_;\n  typedef std::map<int, std::unique_ptr<muduo::net::Channel>> ChannelList;\n  ChannelList channels_;\n\n  void onRead(int sockfd, muduo::Timestamp t);\n  void onTimer();\n  void onQueryResult(int status, struct hostent* result, const Callback& cb);\n  void onSockCreate(int sockfd, int type);\n  void onSockStateChange(int sockfd, bool read, bool write);\n\n  static void ares_host_callback(void* data, int status, int timeouts, struct hostent* hostent);\n  static int ares_sock_create_callback(int sockfd, int type, void* data);\n  static void ares_sock_state_callback(void* data, int sockfd, int read, int write);\n};\n}  // namespace cdns\n\n#endif  // MUDUO_EXAMPLES_CDNS_RESOLVER_H\n"
  },
  {
    "path": "examples/cdns/dns.cc",
    "content": "#include \"examples/cdns/Resolver.h\"\n#include \"muduo/net/EventLoop.h\"\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace cdns;\n\nEventLoop* g_loop;\nint count = 0;\nint total = 0;\n\nvoid quit()\n{\n  g_loop->quit();\n}\n\nvoid resolveCallback(const string& host, const InetAddress& addr)\n{\n  printf(\"resolveCallback %s -> %s\\n\", host.c_str(), addr.toIpPort().c_str());\n  if (++count == total)\n    quit();\n}\n\nvoid resolve(Resolver* res, const string& host)\n{\n  res->resolve(host, std::bind(&resolveCallback, host, _1));\n}\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  loop.runAfter(10, quit);\n  g_loop = &loop;\n  Resolver resolver(&loop,\n                   argc == 1 ? Resolver::kDNSonly : Resolver::kDNSandHostsFile);\n  if (argc == 1)\n  {\n    total = 3;\n    resolve(&resolver, \"www.chenshuo.com\");\n    resolve(&resolver, \"www.example.com\");\n    resolve(&resolver, \"www.google.com\");\n  }\n  else\n  {\n    total = argc-1;\n    for (int i = 1; i < argc; ++i)\n      resolve(&resolver, argv[i]);\n  }\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/curl/CMakeLists.txt",
    "content": "add_library(muduo_curl Curl.cc)\ntarget_link_libraries(muduo_curl muduo_net)\ntarget_link_libraries(muduo_curl curl)\n\ninstall(TARGETS muduo_curl DESTINATION lib)\ninstall(FILES Curl.h DESTINATION include/muduo/curl)\n\nadd_executable(mcurl mcurl.cc)\ntarget_link_libraries(mcurl muduo_curl)\n\nadd_executable(curl_download download.cc)\ntarget_link_libraries(curl_download muduo_curl)\n\n"
  },
  {
    "path": "examples/curl/Curl.cc",
    "content": "#include \"examples/curl/Curl.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <curl/curl.h>\n#include <assert.h>\n\nusing namespace curl;\nusing namespace muduo;\nusing namespace muduo::net;\n\nstatic void dummy(const std::shared_ptr<Channel>&)\n{\n}\n\nRequest::Request(Curl* owner, const char* url)\n  : owner_(owner),\n    curl_(CHECK_NOTNULL(curl_easy_init()))\n{\n  setopt(CURLOPT_URL, url);\n  setopt(CURLOPT_WRITEFUNCTION, &Request::writeData);\n  setopt(CURLOPT_WRITEDATA, this);\n  setopt(CURLOPT_HEADERFUNCTION, &Request::headerData);\n  setopt(CURLOPT_HEADERDATA, this);\n  setopt(CURLOPT_PRIVATE, this);\n  setopt(CURLOPT_USERAGENT, \"curl\");\n  // set useragent\n  LOG_DEBUG << curl_ << \" \" << url;\n  curl_multi_add_handle(owner_->getCurlm(), curl_);\n}\n\nRequest::~Request()\n{\n  assert(!channel_ || channel_->isNoneEvent());\n  curl_multi_remove_handle(owner_->getCurlm(), curl_);\n  curl_easy_cleanup(curl_);\n}\n\n// NOT implemented yet\n//\n// void Request::allowRedirect(int redirects)\n// {\n//   setopt(CURLOPT_FOLLOWLOCATION, 1);\n//   setopt(CURLOPT_MAXREDIRS, redirects);\n// }\n\nvoid Request::headerOnly()\n{\n  setopt(CURLOPT_NOBODY, 1);\n}\n\nvoid Request::setRange(const StringArg range)\n{\n  setopt(CURLOPT_RANGE, range.c_str());\n}\n\nconst char* Request::getEffectiveUrl()\n{\n  const char* p = NULL;\n  curl_easy_getinfo(curl_, CURLINFO_EFFECTIVE_URL, &p);\n  return p;\n}\n\nconst char* Request::getRedirectUrl()\n{\n  const char* p = NULL;\n  curl_easy_getinfo(curl_, CURLINFO_REDIRECT_URL, &p);\n  return p;\n}\n\nint Request::getResponseCode()\n{\n  long code = 0;\n  curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &code);\n  return static_cast<int>(code);\n}\n\nChannel* Request::setChannel(int fd)\n{\n  assert(channel_.get() == NULL);\n  channel_.reset(new Channel(owner_->getLoop(), fd));\n  channel_->tie(shared_from_this());\n  return get_pointer(channel_);\n}\n\nvoid Request::removeChannel()\n{\n  channel_->disableAll();\n  channel_->remove();\n  owner_->getLoop()->queueInLoop(std::bind(dummy, channel_));\n  channel_.reset();\n}\n\nvoid Request::done(int code)\n{\n  if (doneCb_)\n  {\n    doneCb_(this, code);\n  }\n}\n\nvoid Request::dataCallback(const char* buffer, int len)\n{\n  if (dataCb_)\n  {\n    dataCb_(buffer, len);\n  }\n}\n\nvoid Request::headerCallback(const char* buffer, int len)\n{\n  if (headerCb_)\n  {\n    headerCb_(buffer, len);\n  }\n}\n\nsize_t Request::writeData(char* buffer, size_t size, size_t nmemb, void* userp)\n{\n  assert(size == 1);\n  Request* req = static_cast<Request*>(userp);\n  req->dataCallback(buffer, static_cast<int>(nmemb));\n  return nmemb;\n}\n\nsize_t Request::headerData(char* buffer, size_t size, size_t nmemb, void* userp)\n{\n  assert(size == 1);\n  Request* req = static_cast<Request*>(userp);\n  req->headerCallback(buffer, static_cast<int>(nmemb));\n  return nmemb;\n}\n\n// ==================================================================\n\nvoid Curl::initialize(Option opt)\n{\n  curl_global_init(opt == kCURLnossl ? CURL_GLOBAL_NOTHING : CURL_GLOBAL_SSL);\n}\n\nint Curl::socketCallback(CURL* c, int fd, int what, void* userp, void* socketp)\n{\n  Curl* curl = static_cast<Curl*>(userp);\n  const char *whatstr[]={ \"none\", \"IN\", \"OUT\", \"INOUT\", \"REMOVE\" };\n  LOG_DEBUG << \"Curl::socketCallback [\" << curl << \"] - fd = \" << fd\n            << \" what = \" << whatstr[what];\n  Request* req = NULL;\n  curl_easy_getinfo(c, CURLINFO_PRIVATE, &req);\n  assert(req->getCurl() == c);\n  if (what == CURL_POLL_REMOVE)\n  {\n    muduo::net::Channel* ch = static_cast<Channel*>(socketp);\n    assert(req->getChannel() == ch);\n    req->removeChannel();\n    ch = NULL;\n    curl_multi_assign(curl->curlm_, fd, ch);\n  }\n  else\n  {\n    muduo::net::Channel* ch = static_cast<Channel*>(socketp);\n    if (!ch)\n    {\n      ch = req->setChannel(fd);\n      ch->setReadCallback(std::bind(&Curl::onRead, curl, fd));\n      ch->setWriteCallback(std::bind(&Curl::onWrite, curl, fd));\n      ch->enableReading();\n      curl_multi_assign(curl->curlm_, fd, ch);\n      LOG_TRACE << \"new channel for fd=\" << fd;\n    }\n    assert(req->getChannel() == ch);\n    // update\n    if (what & CURL_POLL_OUT)\n    {\n      ch->enableWriting();\n    }\n    else\n    {\n      ch->disableWriting();\n    }\n  }\n  return 0;\n}\n\nint Curl::timerCallback(CURLM* curlm, long ms, void* userp)\n{\n  Curl* curl = static_cast<Curl*>(userp);\n  LOG_DEBUG << curl << \" \" << ms << \" ms\";\n  curl->loop_->runAfter(static_cast<int>(ms)/1000.0, std::bind(&Curl::onTimer, curl));\n  return 0;\n}\n\nCurl::Curl(EventLoop* loop)\n  : loop_(loop),\n    curlm_(CHECK_NOTNULL(curl_multi_init())),\n    runningHandles_(0),\n    prevRunningHandles_(0)\n{\n  curl_multi_setopt(curlm_, CURLMOPT_SOCKETFUNCTION, &Curl::socketCallback);\n  curl_multi_setopt(curlm_, CURLMOPT_SOCKETDATA, this);\n  curl_multi_setopt(curlm_, CURLMOPT_TIMERFUNCTION, &Curl::timerCallback);\n  curl_multi_setopt(curlm_, CURLMOPT_TIMERDATA, this);\n}\n\nCurl::~Curl()\n{\n  curl_multi_cleanup(curlm_);\n}\n\nRequestPtr Curl::getUrl(StringArg url)\n{\n  RequestPtr req(new Request(this, url.c_str()));\n  return req;\n}\n\nvoid Curl::onTimer()\n{\n  CURLMcode rc = CURLM_OK;\n  do {\n    LOG_TRACE;\n    rc = curl_multi_socket_action(curlm_, CURL_SOCKET_TIMEOUT, 0, &runningHandles_);\n    LOG_TRACE << rc << \" \" << runningHandles_;\n  } while (rc == CURLM_CALL_MULTI_PERFORM);\n  checkFinish();\n}\n\nvoid Curl::onRead(int fd)\n{\n  CURLMcode rc = CURLM_OK;\n  do {\n    LOG_TRACE << fd;\n    rc = curl_multi_socket_action(curlm_, fd, CURL_POLL_IN, &runningHandles_);\n    LOG_TRACE << fd << \" \" << rc << \" \" << runningHandles_;\n  } while (rc == CURLM_CALL_MULTI_PERFORM);\n  checkFinish();\n}\n\nvoid Curl::onWrite(int fd)\n{\n  CURLMcode rc = CURLM_OK;\n  do {\n    LOG_TRACE << fd;\n    rc = curl_multi_socket_action(curlm_, fd, CURL_POLL_OUT, &runningHandles_);\n    LOG_TRACE << fd << \" \" << rc << \" \" << runningHandles_;\n  } while (rc == CURLM_CALL_MULTI_PERFORM);\n  checkFinish();\n}\n\nvoid Curl::checkFinish()\n{\n  if (prevRunningHandles_ > runningHandles_ || runningHandles_ == 0)\n  {\n    CURLMsg* msg = NULL;\n    int left = 0;\n    while ( (msg = curl_multi_info_read(curlm_, &left)) != NULL)\n    {\n      if (msg->msg == CURLMSG_DONE)\n      {\n        CURL* c = msg->easy_handle;\n        CURLcode res = msg->data.result;\n        Request* req = NULL;\n        curl_easy_getinfo(c, CURLINFO_PRIVATE, &req);\n        assert(req->getCurl() == c);\n        LOG_TRACE << req << \" done\";\n        req->done(res);\n      }\n    }\n  }\n  prevRunningHandles_ = runningHandles_;\n}\n"
  },
  {
    "path": "examples/curl/Curl.h",
    "content": "#ifndef MUDUO_EXAMPLES_CURL_CURL_H\n#define MUDUO_EXAMPLES_CURL_CURL_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n\n#include \"muduo/net/Callbacks.h\"\n\nextern \"C\"\n{\ntypedef void CURLM;\ntypedef void CURL;\n}\n\nnamespace muduo\n{\nnamespace net\n{\nclass Channel;\nclass EventLoop;\n}\n}\n\nnamespace curl\n{\n\nclass Curl;\n\nclass Request : public std::enable_shared_from_this<Request>,\n                muduo::noncopyable\n{\n public:\n  typedef std::function<void(const char*, int)> DataCallback;\n  typedef std::function<void(Request*, int)> DoneCallback;\n\n  Request(Curl*, const char* url);\n  ~Request();\n\n  void setDataCallback(const DataCallback& cb)\n  { dataCb_ = cb; }\n\n  void setDoneCallback(const DoneCallback& cb)\n  { doneCb_ = cb; }\n\n  void setHeaderCallback(const DataCallback& cb)\n  { headerCb_ = cb; }\n\n  // void allowRedirect(int redirects);\n  void headerOnly();\n  void setRange(const muduo::StringArg range);\n\n  template<typename OPT>\n  int setopt(OPT opt, long p)\n  {\n    return curl_easy_setopt(curl_, opt, p);\n  }\n\n  template<typename OPT>\n  int setopt(OPT opt, const char* p)\n  {\n    return curl_easy_setopt(curl_, opt, p);\n  }\n\n  template<typename OPT>\n  int setopt(OPT opt, void* p)\n  {\n    return curl_easy_setopt(curl_, opt, p);\n  }\n\n  template<typename OPT>\n  int setopt(OPT opt, size_t (*p)(char *, size_t , size_t , void *))\n  {\n    return curl_easy_setopt(curl_, opt, p);\n  }\n\n  const char* getEffectiveUrl();\n  const char* getRedirectUrl();\n  int getResponseCode();\n\n  // internal\n  muduo::net::Channel* setChannel(int fd);\n  void removeChannel();\n  void done(int code);\n  CURL* getCurl() { return curl_; }\n  muduo::net::Channel* getChannel() { return muduo::get_pointer(channel_); }\n\n private:\n\n  void dataCallback(const char* buffer, int len);\n  void headerCallback(const char* buffer, int len);\n  static size_t writeData(char *buffer, size_t size, size_t nmemb, void *userp);\n  static size_t headerData(char *buffer, size_t size, size_t nmemb, void *userp);\n  void doneCallback();\n\n  class Curl* owner_;\n  CURL* curl_;\n  std::shared_ptr<muduo::net::Channel> channel_;\n  DataCallback dataCb_;\n  DataCallback headerCb_;\n  DoneCallback doneCb_;\n};\n\ntypedef std::shared_ptr<Request> RequestPtr;\n\nclass Curl : muduo::noncopyable\n{\n public:\n\n  enum Option\n  {\n    kCURLnossl = 0,\n    kCURLssl   = 1,\n  };\n\n  explicit Curl(muduo::net::EventLoop* loop);\n  ~Curl();\n\n  RequestPtr getUrl(muduo::StringArg url);\n\n  static void initialize(Option opt = kCURLnossl);\n\n  // internal\n  CURLM* getCurlm() { return curlm_; }\n  muduo::net::EventLoop* getLoop() { return loop_; }\n\n private:\n  void onTimer();\n  void onRead(int fd);\n  void onWrite(int fd);\n  void checkFinish();\n\n  static int socketCallback(CURL*, int, int, void*, void*);\n  static int timerCallback(CURLM*, long, void*);\n\n  muduo::net::EventLoop* loop_;\n  CURLM* curlm_;\n  int runningHandles_;\n  int prevRunningHandles_;\n};\n\n}  // namespace curl\n\n#endif  // MUDUO_EXAMPLES_CURL_CURL_H\n"
  },
  {
    "path": "examples/curl/README",
    "content": "This is a proof-of-concept implementation of muduo-curl bridge.\nIt demostrates the simplest use case of curl with muduo.\n\nNote:\n1. DNS resolving could be blocking, if your curl is not built with c-ares.\n2. Request object should survive doneCallback.\n"
  },
  {
    "path": "examples/curl/download.cc",
    "content": "// Concurrent downloading one file from HTTP\n\n#include \"examples/curl/Curl.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include <stdio.h>\n#include <sstream>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::shared_ptr<FILE> FilePtr;\n\ntemplate<int N>\nbool startWith(const string& str, const char (&prefix)[N])\n{\n  return str.size() >= N-1 && std::equal(prefix, prefix+N-1, str.begin());\n}\n\nclass Piece : noncopyable\n{\n public:\n  Piece(const curl::RequestPtr& req,\n        const FilePtr& out,\n        const muduo::string& range,\n        std::function<void()> done)\n    : req_(req),\n      out_(out),\n      range_(range),\n      doneCb_(std::move(done))\n  {\n    LOG_INFO << \"range: \" << range;\n    req->setRange(range);\n    req_->setDataCallback(\n        std::bind(&Piece::onData, this, _1, _2));\n    req_->setDoneCallback(\n        std::bind(&Piece::onDone, this, _1, _2));\n  }\n private:\n  void onData(const char* data, int len)\n  {\n    ::fwrite(data, 1, len, get_pointer(out_));\n  }\n\n  void onDone(curl::Request* c, int code)\n  {\n    LOG_INFO << \"[\" << range_ << \"] is done\";\n    req_.reset();\n    out_.reset();\n    doneCb_();\n  }\n\n  curl::RequestPtr req_;\n  FilePtr out_;\n  muduo::string range_;\n  std::function<void()> doneCb_;\n};\n\nclass Downloader : noncopyable\n{\n public:\n  Downloader(EventLoop* loop, const string& url)\n    : loop_(loop),\n      curl_(loop_),\n      url_(url),\n      req_(curl_.getUrl(url_)),\n      found_(false),\n      acceptRanges_(false),\n      length_(0),\n      pieces_(kConcurrent),\n      concurrent_(0)\n  {\n    req_->setHeaderCallback(\n        std::bind(&Downloader::onHeader, this, _1, _2));\n    req_->setDoneCallback(\n        std::bind(&Downloader::onHeaderDone, this, _1, _2));\n    req_->headerOnly();\n  }\n\n private:\n  void onHeader(const char* data, int len)\n  {\n    string line(data, len);\n    if (startWith(line, \"HTTP/1.1 200\") || startWith(line, \"HTTP/1.0 200\"))\n    {\n      found_ = true;\n    }\n    if (line == \"Accept-Ranges: bytes\\r\\n\")\n    {\n      acceptRanges_ = true;\n      LOG_DEBUG << \"Accept-Ranges\";\n    }\n    else if (startWith(line, \"Content-Length:\"))\n    {\n      length_ = atoll(line.c_str() + strlen(\"Content-Length:\"));\n      LOG_INFO << \"Content-Length: \" << length_;\n    }\n  }\n\n  void onHeaderDone(curl::Request* c, int code)\n  {\n    LOG_DEBUG << code;\n    if (acceptRanges_ && length_ >= kConcurrent * 4096)\n    {\n      LOG_INFO << \"Downloading with \" << kConcurrent << \" connections\";\n      concurrent_ = kConcurrent;\n      concurrentDownload();\n    }\n    else if (found_)\n    {\n      LOG_WARN << \"Single connection download\";\n      FILE* fp = ::fopen(\"output\", \"wb\");\n      if (fp)\n      {\n        FilePtr(fp, ::fclose).swap(out_);\n        req_.reset();\n        req2_ = curl_.getUrl(url_);\n        req2_->setDataCallback(\n            std::bind(&Downloader::onData, this, _1, _2));\n        req2_->setDoneCallback(\n            std::bind(&Downloader::onDownloadDone, this));\n        concurrent_ = 1;\n      }\n      else\n      {\n        LOG_ERROR << \"Can not create output file\";\n        loop_->quit();\n      }\n    }\n    else\n    {\n      LOG_ERROR << \"File not found\";\n      loop_->quit();\n    }\n  }\n\n  void concurrentDownload()\n  {\n    const int64_t pieceLen = length_ / kConcurrent;\n    for (int i = 0; i < kConcurrent; ++i)\n    {\n      char buf[256];\n      snprintf(buf, sizeof buf, \"output-%05d-of-%05d\", i, kConcurrent);\n      FILE* fp = ::fopen(buf, \"wb\");\n      if (fp)\n      {\n        FilePtr out(fp, ::fclose);\n        curl::RequestPtr req = curl_.getUrl(url_);\n\n        std::ostringstream range;\n        if (i < kConcurrent - 1)\n        {\n          range << i * pieceLen << \"-\" << (i+1) * pieceLen - 1;\n        }\n        else\n        {\n          range << i * pieceLen << \"-\" << length_ - 1;\n        }\n        pieces_[i].reset(new Piece(req,\n                                   out,\n                                   range.str(),\n                                   std::bind(&Downloader::onDownloadDone, this)));\n      }\n      else\n      {\n        LOG_ERROR << \"Can not create output file: \" << buf;\n        loop_->quit();\n      }\n    }\n  }\n\n  void onData(const char* data, int len)\n  {\n    ::fwrite(data, 1, len, get_pointer(out_));\n  }\n\n  void onDownloadDone()\n  {\n    if (--concurrent_ <= 0)\n    {\n      loop_->quit();\n    }\n  }\n\n  EventLoop* loop_;\n  curl::Curl curl_;\n  string url_;\n  curl::RequestPtr req_;\n  curl::RequestPtr req2_;\n  bool found_;\n  bool acceptRanges_;\n  int64_t length_;\n  FilePtr out_;\n  std::vector<std::unique_ptr<Piece>> pieces_;\n  int concurrent_;\n\n  const static int kConcurrent = 4;\n};\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  curl::Curl::initialize(curl::Curl::kCURLssl);\n  string url = argc > 1 ? argv[1] : \"https://chenshuo-public.s3.amazonaws.com/pdf/allinone.pdf\";\n  Downloader d(&loop, url);\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/curl/mcurl.cc",
    "content": "#include \"examples/curl/Curl.h\"\n#include \"muduo/net/EventLoop.h\"\n#include <stdio.h>\n\nusing namespace muduo::net;\n\nEventLoop* g_loop = NULL;\n\nvoid onData(const char* data, int len)\n{\n  printf(\"len %d\\n\", len);\n}\n\nvoid done(curl::Request* c, int code)\n{\n  printf(\"done %p %s %d\\n\", c, c->getEffectiveUrl(), code);\n}\n\nvoid done2(curl::Request* c, int code)\n{\n  printf(\"done2 %p %s %d %d\\n\", c, c->getRedirectUrl(), c->getResponseCode(), code);\n  // g_loop->quit();\n}\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  g_loop = &loop;\n  loop.runAfter(30.0, std::bind(&EventLoop::quit, &loop));\n  curl::Curl::initialize(curl::Curl::kCURLssl);\n  curl::Curl curl(&loop);\n\n  curl::RequestPtr req = curl.getUrl(\"http://chenshuo.com\");\n  req->setDataCallback(onData);\n  req->setDoneCallback(done);\n\n  curl::RequestPtr req2 = curl.getUrl(\"https://github.com\");\n  // req2->allowRedirect(5);\n  req2->setDataCallback(onData);\n  req2->setDoneCallback(done);\n\n  curl::RequestPtr req3 = curl.getUrl(\"http://example.com\");\n  // req3->allowRedirect(5);\n  req3->setDataCallback(onData);\n  req3->setDoneCallback(done2);\n\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/fastcgi/CMakeLists.txt",
    "content": "add_executable(fastcgi_test fastcgi.cc fastcgi_test.cc ../sudoku/sudoku.cc)\ntarget_link_libraries(fastcgi_test muduo_net)\n\n"
  },
  {
    "path": "examples/fastcgi/fastcgi.cc",
    "content": "#include \"examples/fastcgi/fastcgi.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n\nstruct FastCgiCodec::RecordHeader\n{\n  uint8_t version;\n  uint8_t type;\n  uint16_t id;\n  uint16_t length;\n  uint8_t padding;\n  uint8_t unused;\n};\n\nconst unsigned FastCgiCodec::kRecordHeader = static_cast<unsigned>(sizeof(FastCgiCodec::RecordHeader));\n\nenum FcgiType\n{\n  kFcgiInvalid = 0,\n  kFcgiBeginRequest = 1,\n  kFcgiAbortRequest = 2,\n  kFcgiEndRequest = 3,\n  kFcgiParams = 4,\n  kFcgiStdin = 5,\n  kFcgiStdout = 6,\n  kFcgiStderr = 7,\n  kFcgiData = 8,\n  kFcgiGetValues = 9,\n  kFcgiGetValuesResult = 10,\n};\n\nenum FcgiRole\n{\n  // kFcgiInvalid = 0,\n  kFcgiResponder = 1,\n  kFcgiAuthorizer = 2,\n};\n\nenum FcgiConstant\n{\n  kFcgiKeepConn = 1,\n};\n\nusing namespace muduo::net;\n\nbool FastCgiCodec::onParams(const char* content, uint16_t length)\n{\n  if (length > 0)\n  {\n    paramsStream_.append(content, length);\n  }\n  else if (!parseAllParams())\n  {\n    LOG_ERROR << \"parseAllParams() failed\";\n    return false;\n  }\n  return true;\n}\n\nvoid FastCgiCodec::onStdin(const char* content, uint16_t length)\n{\n  if (length > 0)\n  {\n    stdin_.append(content, length);\n  }\n  else\n  {\n    gotRequest_ = true;\n  }\n}\n\nbool FastCgiCodec::parseAllParams()\n{\n  while (paramsStream_.readableBytes() > 0)\n  {\n    uint32_t nameLen = readLen();\n    if (nameLen == static_cast<uint32_t>(-1))\n      return false;\n    uint32_t valueLen = readLen();\n    if (valueLen == static_cast<uint32_t>(-1))\n      return false;\n    if (paramsStream_.readableBytes() >= nameLen+valueLen)\n    {\n      std::string name = paramsStream_.retrieveAsString(nameLen);\n      params_[name] = paramsStream_.retrieveAsString(valueLen);\n    }\n    else\n    {\n      return false;\n    }\n  }\n  return true;\n}\n\nuint32_t FastCgiCodec::readLen()\n{\n  if (paramsStream_.readableBytes() >= 1)\n  {\n    uint8_t byte = paramsStream_.peekInt8();\n    if (byte & 0x80)\n    {\n      if (paramsStream_.readableBytes() >= sizeof(uint32_t))\n      {\n        return paramsStream_.readInt32() & 0x7fffffff;\n      }\n      else\n      {\n        return -1;\n      }\n    }\n    else\n    {\n      return paramsStream_.readInt8();\n    }\n  }\n  else\n  {\n    return -1;\n  }\n}\n\nusing muduo::net::Buffer;\n\nvoid FastCgiCodec::endStdout(Buffer* buf)\n{\n  RecordHeader header =\n  {\n    1,\n    kFcgiStdout,\n    sockets::hostToNetwork16(1),\n    0,\n    0,\n    0,\n  };\n  buf->append(&header, kRecordHeader);\n}\n\nvoid FastCgiCodec::endRequest(Buffer* buf)\n{\n  RecordHeader header =\n  {\n    1,\n    kFcgiEndRequest,\n    sockets::hostToNetwork16(1),\n    sockets::hostToNetwork16(kRecordHeader),\n    0,\n    0,\n  };\n  buf->append(&header, kRecordHeader);\n  buf->appendInt32(0);\n  buf->appendInt32(0);\n}\n\nvoid FastCgiCodec::respond(Buffer* response)\n{\n  if (response->readableBytes() < 65536\n      && response->prependableBytes() >= kRecordHeader)\n  {\n    RecordHeader header =\n    {\n      1,\n      kFcgiStdout,\n      sockets::hostToNetwork16(1),\n      sockets::hostToNetwork16(static_cast<uint16_t>(response->readableBytes())),\n      static_cast<uint8_t>(-response->readableBytes() & 7),\n      0,\n    };\n    response->prepend(&header, kRecordHeader);\n    response->append(\"\\0\\0\\0\\0\\0\\0\\0\\0\", header.padding);\n  }\n  else\n  {\n    // FIXME:\n  }\n\n  endStdout(response);\n  endRequest(response);\n}\n\nbool FastCgiCodec::parseRequest(Buffer* buf)\n{\n  while (buf->readableBytes() >= kRecordHeader)\n  {\n    RecordHeader header;\n    memcpy(&header, buf->peek(), kRecordHeader);\n    header.id = sockets::networkToHost16(header.id);\n    header.length = sockets::networkToHost16(header.length);\n    size_t total = kRecordHeader + header.length + header.padding;\n    if (buf->readableBytes() >= total)\n    {\n      switch (header.type)\n      {\n        case kFcgiBeginRequest:\n          onBeginRequest(header, buf);\n          // FIXME: check\n          break;\n        case kFcgiParams:\n          onParams(buf->peek() + kRecordHeader, header.length);\n          // FIXME: check\n          break;\n        case kFcgiStdin:\n          onStdin(buf->peek() + kRecordHeader, header.length);\n          break;\n        case kFcgiData:\n          // FIXME:\n          break;\n        case kFcgiGetValues:\n          // FIXME:\n          break;\n        default:\n          // FIXME:\n          break;\n      }\n      buf->retrieve(total);\n    }\n    else\n    {\n      break;\n    }\n  }\n  return true;\n}\n\nuint16_t readInt16(const void* p)\n{\n  uint16_t be16 = 0;\n  ::memcpy(&be16, p, sizeof be16);\n  return sockets::networkToHost16(be16);\n}\n\nbool FastCgiCodec::onBeginRequest(const RecordHeader& header, const Buffer* buf)\n{\n  assert(buf->readableBytes() >= header.length);\n  assert(header.type == kFcgiBeginRequest);\n\n  if (header.length >= kRecordHeader)\n  {\n    uint16_t role = readInt16(buf->peek()+kRecordHeader);\n    uint8_t flags = buf->peek()[kRecordHeader + sizeof(int16_t)];\n    if (role == kFcgiResponder)\n    {\n      keepConn_ = flags == kFcgiKeepConn;\n      return true;\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "examples/fastcgi/fastcgi.h",
    "content": "#ifndef MUDUO_EXAMPLES_FASTCGI_FASTCGI_H\n#define MUDUO_EXAMPLES_FASTCGI_FASTCGI_H\n\n#include \"muduo/net/TcpConnection.h\"\n#include <map>\n\n// one FastCgiCodec per TcpConnection\n// both lighttpd and nginx do not implement multiplexing,\n// so there is no concurrent requests of one connection.\nclass FastCgiCodec : muduo::noncopyable\n{\n public:\n  typedef std::map<std::string, std::string> ParamMap;\n  typedef std::function<void (const muduo::net::TcpConnectionPtr& conn,\n                                ParamMap&,\n                                muduo::net::Buffer*)> Callback;\n\n  explicit FastCgiCodec(const Callback& cb)\n    : cb_(cb),\n      gotRequest_(false),\n      keepConn_(false)\n  {\n  }\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp receiveTime)\n  {\n    parseRequest(buf);\n    if (gotRequest_)\n    {\n      cb_(conn, params_, &stdin_);\n      stdin_.retrieveAll();\n      paramsStream_.retrieveAll();\n      params_.clear();\n      gotRequest_ = false;\n      if (!keepConn_)\n      {\n        conn->shutdown();\n      }\n    }\n  }\n\n  static void respond(muduo::net::Buffer* response);\n\n private:\n  struct RecordHeader;\n  bool parseRequest(muduo::net::Buffer* buf);\n  bool onBeginRequest(const RecordHeader& header, const muduo::net::Buffer* buf);\n  void onStdin(const char* content, uint16_t length);\n  bool onParams(const char* content, uint16_t length);\n  bool parseAllParams();\n  uint32_t readLen();\n\n  static void endStdout(muduo::net::Buffer* buf);\n  static void endRequest(muduo::net::Buffer* buf);\n\n  Callback cb_;\n  bool gotRequest_;\n  bool keepConn_;\n  muduo::net::Buffer stdin_;\n  muduo::net::Buffer paramsStream_;\n  ParamMap params_;\n\n  const static unsigned kRecordHeader;\n};\n\n#endif  // MUDUO_EXAMPLES_FASTCGI_FASTCGI_H\n"
  },
  {
    "path": "examples/fastcgi/fastcgi_test.cc",
    "content": "#include \"examples/fastcgi/fastcgi.h\"\n#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst string kPath = \"/sudoku/\";\n\nvoid onRequest(const TcpConnectionPtr& conn,\n               FastCgiCodec::ParamMap& params,\n               Buffer* in)\n{\n  string uri = params[\"REQUEST_URI\"];\n  LOG_INFO << conn->name() << \": \" << uri;\n\n  for (FastCgiCodec::ParamMap::const_iterator it = params.begin();\n       it != params.end(); ++it)\n  {\n    LOG_DEBUG << it->first << \" = \" << it->second;\n  }\n  if (in->readableBytes() > 0)\n    LOG_DEBUG << \"stdin \" << in->retrieveAllAsString();\n  Buffer response;\n  response.append(\"Context-Type: text/plain\\r\\n\\r\\n\");\n  if (uri.size() == kCells + kPath.size() && uri.find(kPath) == 0)\n  {\n    response.append(solveSudoku(uri.substr(kPath.size())));\n  }\n  else\n  {\n    // FIXME: set http status code 400\n    response.append(\"bad request\");\n  }\n\n  FastCgiCodec::respond(&response);\n  conn->send(&response);\n}\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    typedef std::shared_ptr<FastCgiCodec> CodecPtr;\n    CodecPtr codec(new FastCgiCodec(onRequest));\n    conn->setContext(codec);\n    conn->setMessageCallback(\n        std::bind(&FastCgiCodec::onMessage, codec, _1, _2, _3));\n    conn->setTcpNoDelay(true);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  int port = 19981;\n  int threads = 0;\n  if (argc > 1)\n    port = atoi(argv[1]);\n  if (argc > 2)\n    threads = atoi(argv[2]);\n  InetAddress addr(static_cast<uint16_t>(port));\n  LOG_INFO << \"Sudoku FastCGI listens on \" << addr.toIpPort()\n           << \" threads \" << threads;\n  muduo::net::EventLoop loop;\n  TcpServer server(&loop, addr, \"FastCGI\");\n  server.setConnectionCallback(onConnection);\n  server.setThreadNum(threads);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/fastcgi/nginx.conf",
    "content": "\n#user  nobody;\nworker_processes  1;\n\n#error_log  logs/error.log;\n#error_log  logs/error.log  notice;\n#error_log  logs/error.log  info;\n\n#pid        logs/nginx.pid;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    #include       mime.types;\n    default_type  application/octet-stream;\n\n    #log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n    #                  '$status $body_bytes_sent \"$http_referer\" '\n    #                  '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    #access_log  logs/access.log  main;\n    access_log      off;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    keepalive_timeout  65;\n\n    #gzip  on;\n\n    upstream muduo_backend {\n        server localhost:19981;\n        #server localhost:19982;\n        keepalive 32;\n    }\n\n    server {\n        listen       10080;\n        server_name  localhost;\n\n        #access_log  logs/host.access.log  main;\n\n        location / {\n            root   html;\n            index  index.html index.htm;\n        }\n\n        #error_page  404              /404.html;\n\n        # redirect server error pages to the static page /50x.html\n        #\n        error_page   500 502 503 504  /50x.html;\n        location = /50x.html {\n            root   html;\n        }\n\n        # pass /sudoku/ to muduo FastCGI server listening on 127.0.0.1:19981\n        #\n        location /sudoku/  {\n            fastcgi_keep_conn   on;\n            fastcgi_pass        muduo_backend;\n            #include             fastcgi_params;\n            #fastcgi_param  QUERY_STRING       $query_string;\n            #fastcgi_param  REQUEST_METHOD     $request_method;\n            #fastcgi_param  CONTENT_TYPE       $content_type;\n            #fastcgi_param  CONTENT_LENGTH     $content_length;\n\n            #fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\n            fastcgi_param  REQUEST_URI        $request_uri;\n            #fastcgi_param  DOCUMENT_URI       $document_uri;\n            #fastcgi_param  DOCUMENT_ROOT      $document_root;\n            #fastcgi_param  SERVER_PROTOCOL    $server_protocol;\n            #fastcgi_param  HTTPS              $https if_not_empty;\n\n            #fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\n            #fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\n            #fastcgi_param  REMOTE_ADDR        $remote_addr;\n            #fastcgi_param  REMOTE_PORT        $remote_port;\n            #fastcgi_param  SERVER_ADDR        $server_addr;\n            #fastcgi_param  SERVER_PORT        $server_port;\n            #fastcgi_param  SERVER_NAME        $server_name;\n        }\n\n        # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n        #\n        #location ~ \\.php$ {\n        #    proxy_pass   http://127.0.0.1;\n        #}\n\n        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n        #\n        #location ~ \\.php$ {\n        #    root           html;\n        #    fastcgi_pass   127.0.0.1:9000;\n        #    fastcgi_index  index.php;\n        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;\n        #    include        fastcgi_params;\n        #}\n\n        # deny access to .htaccess files, if Apache's document root\n        # concurs with nginx's one\n        #\n        #location ~ /\\.ht {\n        #    deny  all;\n        #}\n    }\n}\n"
  },
  {
    "path": "examples/filetransfer/CMakeLists.txt",
    "content": "add_executable(filetransfer_download download.cc)\ntarget_link_libraries(filetransfer_download muduo_net)\n\nadd_executable(filetransfer_download2 download2.cc)\ntarget_link_libraries(filetransfer_download2 muduo_net)\n\nadd_executable(filetransfer_download3 download3.cc)\ntarget_link_libraries(filetransfer_download3 muduo_net)\n\n"
  },
  {
    "path": "examples/filetransfer/download.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst char* g_file = NULL;\n\n// FIXME: use FileUtil::readFile()\nstring readFile(const char* filename)\n{\n  string content;\n  FILE* fp = ::fopen(filename, \"rb\");\n  if (fp)\n  {\n    // inefficient!!!\n    const int kBufSize = 1024*1024;\n    char iobuf[kBufSize];\n    ::setbuffer(fp, iobuf, sizeof iobuf);\n\n    char buf[kBufSize];\n    size_t nread = 0;\n    while ( (nread = ::fread(buf, 1, sizeof buf, fp)) > 0)\n    {\n      content.append(buf, nread);\n    }\n    ::fclose(fp);\n  }\n  return content;\n}\n\nvoid onHighWaterMark(const TcpConnectionPtr& conn, size_t len)\n{\n  LOG_INFO << \"HighWaterMark \" << len;\n}\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"FileServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    LOG_INFO << \"FileServer - Sending file \" << g_file\n             << \" to \" << conn->peerAddress().toIpPort();\n    conn->setHighWaterMarkCallback(onHighWaterMark, 64*1024);\n    string fileContent = readFile(g_file);\n    conn->send(fileContent);\n    conn->shutdown();\n    LOG_INFO << \"FileServer - done\";\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    g_file = argv[1];\n\n    EventLoop loop;\n    InetAddress listenAddr(2021);\n    TcpServer server(&loop, listenAddr, \"FileServer\");\n    server.setConnectionCallback(onConnection);\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    fprintf(stderr, \"Usage: %s file_for_downloading\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/filetransfer/download2.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onHighWaterMark(const TcpConnectionPtr& conn, size_t len)\n{\n  LOG_INFO << \"HighWaterMark \" << len;\n}\n\nconst int kBufSize = 64*1024;\nconst char* g_file = NULL;\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"FileServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    LOG_INFO << \"FileServer - Sending file \" << g_file\n             << \" to \" << conn->peerAddress().toIpPort();\n    conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1);\n\n    FILE* fp = ::fopen(g_file, \"rb\");\n    if (fp)\n    {\n      conn->setContext(fp);\n      char buf[kBufSize];\n      size_t nread = ::fread(buf, 1, sizeof buf, fp);\n      conn->send(buf, static_cast<int>(nread));\n    }\n    else\n    {\n      conn->shutdown();\n      LOG_INFO << \"FileServer - no such file\";\n    }\n  }\n  else\n  {\n    if (!conn->getContext().empty())\n    {\n      FILE* fp = boost::any_cast<FILE*>(conn->getContext());\n      if (fp)\n      {\n        ::fclose(fp);\n      }\n    }\n  }\n}\n\nvoid onWriteComplete(const TcpConnectionPtr& conn)\n{\n  FILE* fp = boost::any_cast<FILE*>(conn->getContext());\n  char buf[kBufSize];\n  size_t nread = ::fread(buf, 1, sizeof buf, fp);\n  if (nread > 0)\n  {\n    conn->send(buf, static_cast<int>(nread));\n  }\n  else\n  {\n    ::fclose(fp);\n    fp = NULL;\n    conn->setContext(fp);\n    conn->shutdown();\n    LOG_INFO << \"FileServer - done\";\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    g_file = argv[1];\n\n    EventLoop loop;\n    InetAddress listenAddr(2021);\n    TcpServer server(&loop, listenAddr, \"FileServer\");\n    server.setConnectionCallback(onConnection);\n    server.setWriteCompleteCallback(onWriteComplete);\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    fprintf(stderr, \"Usage: %s file_for_downloading\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/filetransfer/download3.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onHighWaterMark(const TcpConnectionPtr& conn, size_t len)\n{\n  LOG_INFO << \"HighWaterMark \" << len;\n}\n\nconst int kBufSize = 64*1024;\nconst char* g_file = NULL;\ntypedef std::shared_ptr<FILE> FilePtr;\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"FileServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    LOG_INFO << \"FileServer - Sending file \" << g_file\n             << \" to \" << conn->peerAddress().toIpPort();\n    conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1);\n\n    FILE* fp = ::fopen(g_file, \"rb\");\n    if (fp)\n    {\n      FilePtr ctx(fp, ::fclose);\n      conn->setContext(ctx);\n      char buf[kBufSize];\n      size_t nread = ::fread(buf, 1, sizeof buf, fp);\n      conn->send(buf, static_cast<int>(nread));\n    }\n    else\n    {\n      conn->shutdown();\n      LOG_INFO << \"FileServer - no such file\";\n    }\n  }\n}\n\nvoid onWriteComplete(const TcpConnectionPtr& conn)\n{\n  const FilePtr& fp = boost::any_cast<const FilePtr&>(conn->getContext());\n  char buf[kBufSize];\n  size_t nread = ::fread(buf, 1, sizeof buf, get_pointer(fp));\n  if (nread > 0)\n  {\n    conn->send(buf, static_cast<int>(nread));\n  }\n  else\n  {\n    conn->shutdown();\n    LOG_INFO << \"FileServer - done\";\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    g_file = argv[1];\n\n    EventLoop loop;\n    InetAddress listenAddr(2021);\n    TcpServer server(&loop, listenAddr, \"FileServer\");\n    server.setConnectionCallback(onConnection);\n    server.setWriteCompleteCallback(onWriteComplete);\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    fprintf(stderr, \"Usage: %s file_for_downloading\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/filetransfer/loadtest/Client.java",
    "content": "import java.net.InetSocketAddress;\nimport java.util.Random;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executors;\n\nimport org.jboss.netty.bootstrap.ClientBootstrap;\nimport org.jboss.netty.channel.ChannelFactory;\nimport org.jboss.netty.channel.ChannelPipeline;\nimport org.jboss.netty.channel.ChannelPipelineFactory;\nimport org.jboss.netty.channel.Channels;\nimport org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;\n\npublic class Client {\n\n    private static final class PipelineFactory implements ChannelPipelineFactory {\n        private final int kMinLength;\n        private final int kMaxLength;\n        private final CountDownLatch latch;\n        Random random = new Random();\n\n        private PipelineFactory(int kMinLength, int kMaxLength, CountDownLatch latch) {\n            this.kMinLength = kMinLength;\n            this.kMaxLength = kMaxLength;\n            this.latch = latch;\n            assert kMinLength <= kMaxLength;\n        }\n\n        @Override\n        public ChannelPipeline getPipeline() throws Exception {\n            int variance = random.nextInt(kMaxLength - kMinLength + 1);\n            int maxLength = kMinLength + variance;\n            return Channels.pipeline(new Handler(maxLength, latch));\n        }\n    }\n\n    static final int kClients = 500;\n    static final int kMB = 1024 * 1024;\n    static final int kMinLength = 1 * kMB;\n    static final int kMaxLength = 6 * kMB;\n\n    public static void main(String[] args) throws Exception {\n        ChannelFactory channelFactory = new NioClientSocketChannelFactory(\n                Executors.newCachedThreadPool(),\n                Executors.newCachedThreadPool());\n        long start = System.currentTimeMillis();\n\n        final CountDownLatch latch = new CountDownLatch(kClients);\n        ChannelPipelineFactory pipelineFactory = new PipelineFactory(kMinLength, kMaxLength, latch);\n        for (int i = 0; i < kClients; ++i) {\n            ClientBootstrap bootstrap = new ClientBootstrap(channelFactory);\n            bootstrap.setPipelineFactory(pipelineFactory);\n            bootstrap.connect(new InetSocketAddress(args[0], 2021));\n        }\n\n        latch.await();\n\n        System.out.println(Thread.currentThread().getId() + \" All done. \"\n                + (System.currentTimeMillis() - start));\n        System.exit(0);\n    }\n\n}\n"
  },
  {
    "path": "examples/filetransfer/loadtest/Handler.java",
    "content": "import java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.jboss.netty.buffer.BigEndianHeapChannelBuffer;\nimport org.jboss.netty.channel.Channel;\nimport org.jboss.netty.channel.ChannelHandlerContext;\nimport org.jboss.netty.channel.ChannelStateEvent;\nimport org.jboss.netty.channel.ExceptionEvent;\nimport org.jboss.netty.channel.MessageEvent;\nimport org.jboss.netty.channel.SimpleChannelUpstreamHandler;\n\npublic class Handler extends SimpleChannelUpstreamHandler {\n\n    private static int created = 0;\n    private int received = 0;\n    private final int maxLength;\n    private int id;\n    private CountDownLatch latch;\n    private MessageDigest digest;\n\n    public Handler(int maxLength, CountDownLatch latch) throws Exception {\n        this.id = created++;\n        this.maxLength = maxLength;\n        this.latch = latch;\n        this.digest = MessageDigest.getInstance(\"MD5\");\n        System.out.println(\"Handler tid=\" + Thread.currentThread().getId() + \" \" + id);\n    }\n\n    @Override\n    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n            throws Exception {\n        System.out.println(\"channelConnected tid=\" + Thread.currentThread().getId() + \" \" + id);\n    }\n\n    @Override\n    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n            throws Exception {\n        byte[] md5 = digest.digest();\n        BigInteger bigInt = new BigInteger(1, md5);\n        System.out.println(\"channelDisconnected tid=\" + Thread.currentThread().getId() + \" \" + id\n                + \" got \"\n                + received + \" \" + bigInt.toString(16));\n        latch.countDown();\n    }\n\n    @Override\n    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {\n        BigEndianHeapChannelBuffer message = (BigEndianHeapChannelBuffer) e.getMessage();\n        // System.out.println(\"messageReceived \" + ctx.getChannel() + message.readableBytes());\n        received += message.readableBytes();\n        digest.update(message.array(), message.readerIndex(), message.readableBytes());\n        if (received > maxLength) {\n            System.out.println(\"messageReceived tid=\" + Thread.currentThread().getId()\n                    + \" \" + id + \" got \" + received);\n            ctx.getChannel().close();\n        }\n    }\n\n    @Override\n    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {\n        e.getCause().printStackTrace();\n\n        Channel ch = e.getChannel();\n        ch.close();\n        latch.countDown();\n    }\n}\n"
  },
  {
    "path": "examples/hub/CMakeLists.txt",
    "content": "add_executable(hub hub.cc codec.cc)\ntarget_link_libraries(hub muduo_inspect)\n\nadd_library(muduo_pubsub pubsub.cc codec.cc)\ntarget_link_libraries(muduo_pubsub muduo_net)\n\nadd_executable(pub pub.cc)\ntarget_link_libraries(pub muduo_pubsub)\n\nadd_executable(sub sub.cc)\ntarget_link_libraries(sub muduo_pubsub)\n\n"
  },
  {
    "path": "examples/hub/README",
    "content": "hub - a server for broadcasting\npubsub - a client library of hub\npub - a command line tool for publishing content on a topic\nsub - a demo tool for subscribing a topic\n\n"
  },
  {
    "path": "examples/hub/codec.cc",
    "content": "#include \"examples/hub/codec.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace pubsub;\n\nParseResult pubsub::parseMessage(Buffer* buf,\n                                 string* cmd,\n                                 string* topic,\n                                 string* content)\n{\n  ParseResult result = kError;\n  const char* crlf = buf->findCRLF();\n  if (crlf)\n  {\n    const char* space = std::find(buf->peek(), crlf, ' ');\n    if (space != crlf)\n    {\n      cmd->assign(buf->peek(), space);\n      topic->assign(space+1, crlf);\n      if (*cmd == \"pub\")\n      {\n        const char* start = crlf + 2;\n        crlf = buf->findCRLF(start);\n        if (crlf)\n        {\n          content->assign(start, crlf);\n          buf->retrieveUntil(crlf+2);\n          result = kSuccess;\n        }\n        else\n        {\n          result = kContinue;\n        }\n      }\n      else\n      {\n        buf->retrieveUntil(crlf+2);\n        result = kSuccess;\n      }\n    }\n    else\n    {\n      result = kError;\n    }\n  }\n  else\n  {\n    result = kContinue;\n  }\n  return result;\n}\n\n"
  },
  {
    "path": "examples/hub/codec.h",
    "content": "#ifndef MUDUO_EXAMPLES_HUB_CODEC_H\n#define MUDUO_EXAMPLES_HUB_CODEC_H\n\n// internal header file\n\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/Buffer.h\"\n\nnamespace pubsub\n{\nusing muduo::string;\n\nenum ParseResult\n{\n  kError,\n  kSuccess,\n  kContinue,\n};\n\nParseResult parseMessage(muduo::net::Buffer* buf,\n                         string* cmd,\n                         string* topic,\n                         string* content);\n}  // namespace pubsub\n\n#endif  // MUDUO_EXAMPLES_HUB_CODEC_H\n\n"
  },
  {
    "path": "examples/hub/hub.cc",
    "content": "#include \"examples/hub/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <map>\n#include <set>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace pubsub\n{\n\ntypedef std::set<string> ConnectionSubscription;\n\nclass Topic : public muduo::copyable\n{\n public:\n  Topic(const string& topic)\n    : topic_(topic)\n  {\n  }\n\n  void add(const TcpConnectionPtr& conn)\n  {\n    audiences_.insert(conn);\n    if (lastPubTime_.valid())\n    {\n      conn->send(makeMessage());\n    }\n  }\n\n  void remove(const TcpConnectionPtr& conn)\n  {\n    audiences_.erase(conn);\n  }\n\n  void publish(const string& content, Timestamp time)\n  {\n    content_ = content;\n    lastPubTime_ = time;\n    string message = makeMessage();\n    for (std::set<TcpConnectionPtr>::iterator it = audiences_.begin();\n         it != audiences_.end();\n         ++it)\n    {\n      (*it)->send(message);\n    }\n  }\n\n private:\n\n  string makeMessage()\n  {\n    return \"pub \" + topic_ + \"\\r\\n\" + content_ + \"\\r\\n\";\n  }\n\n  string topic_;\n  string content_;\n  Timestamp lastPubTime_;\n  std::set<TcpConnectionPtr> audiences_;\n};\n\nclass PubSubServer : noncopyable\n{\n public:\n  PubSubServer(muduo::net::EventLoop* loop,\n               const muduo::net::InetAddress& listenAddr)\n    : loop_(loop),\n      server_(loop, listenAddr, \"PubSubServer\")\n  {\n    server_.setConnectionCallback(\n        std::bind(&PubSubServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&PubSubServer::onMessage, this, _1, _2, _3));\n    loop_->runEvery(1.0, std::bind(&PubSubServer::timePublish, this));\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      conn->setContext(ConnectionSubscription());\n    }\n    else\n    {\n      const ConnectionSubscription& connSub\n        = boost::any_cast<const ConnectionSubscription&>(conn->getContext());\n      // subtle: doUnsubscribe will erase *it, so increase before calling.\n      for (ConnectionSubscription::const_iterator it = connSub.begin();\n           it != connSub.end();)\n      {\n        doUnsubscribe(conn, *it++);\n      }\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp receiveTime)\n  {\n    ParseResult result = kSuccess;\n    while (result == kSuccess)\n    {\n      string cmd;\n      string topic;\n      string content;\n      result = parseMessage(buf, &cmd, &topic, &content);\n      if (result == kSuccess)\n      {\n        if (cmd == \"pub\")\n        {\n          doPublish(conn->name(), topic, content, receiveTime);\n        }\n        else if (cmd == \"sub\")\n        {\n          LOG_INFO << conn->name() << \" subscribes \" << topic;\n          doSubscribe(conn, topic);\n        }\n        else if (cmd == \"unsub\")\n        {\n          doUnsubscribe(conn, topic);\n        }\n        else\n        {\n          conn->shutdown();\n          result = kError;\n        }\n      }\n      else if (result == kError)\n      {\n        conn->shutdown();\n      }\n    }\n  }\n\n  void timePublish()\n  {\n    Timestamp now = Timestamp::now();\n    doPublish(\"internal\", \"utc_time\", now.toFormattedString(), now);\n  }\n\n  void doSubscribe(const TcpConnectionPtr& conn,\n                   const string& topic)\n  {\n    ConnectionSubscription* connSub\n      = boost::any_cast<ConnectionSubscription>(conn->getMutableContext());\n\n    connSub->insert(topic);\n    getTopic(topic).add(conn);\n  }\n\n  void doUnsubscribe(const TcpConnectionPtr& conn,\n                     const string& topic)\n  {\n    LOG_INFO << conn->name() << \" unsubscribes \" << topic;\n    getTopic(topic).remove(conn);\n    // topic could be the one to be destroyed, so don't use it after erasing.\n    ConnectionSubscription* connSub\n      = boost::any_cast<ConnectionSubscription>(conn->getMutableContext());\n    connSub->erase(topic);\n  }\n\n  void doPublish(const string& source,\n                 const string& topic,\n                 const string& content,\n                 Timestamp time)\n  {\n    getTopic(topic).publish(content, time);\n  }\n\n  Topic& getTopic(const string& topic)\n  {\n    std::map<string, Topic>::iterator it = topics_.find(topic);\n    if (it == topics_.end())\n    {\n      it = topics_.insert(make_pair(topic, Topic(topic))).first;\n    }\n    return it->second;\n  }\n\n  EventLoop* loop_;\n  TcpServer server_;\n  std::map<string, Topic> topics_;\n};\n\n}  // namespace pubsub\n\nint main(int argc, char* argv[])\n{\n  if (argc > 1)\n  {\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    EventLoop loop;\n    if (argc > 2)\n    {\n      //int inspectPort = atoi(argv[2]);\n    }\n    pubsub::PubSubServer server(&loop, InetAddress(port));\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s pubsub_port [inspect_port]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/hub/pub.cc",
    "content": "#include \"examples/hub/pubsub.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n\n#include <iostream>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace pubsub;\n\nEventLoop* g_loop = NULL;\nstring g_topic;\nstring g_content;\n\nvoid connection(PubSubClient* client)\n{\n  if (client->connected())\n  {\n    client->publish(g_topic, g_content);\n    client->stop();\n  }\n  else\n  {\n    g_loop->quit();\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc == 4)\n  {\n    string hostport = argv[1];\n    size_t colon = hostport.find(':');\n    if (colon != string::npos)\n    {\n      string hostip = hostport.substr(0, colon);\n      uint16_t port = static_cast<uint16_t>(atoi(hostport.c_str()+colon+1));\n      g_topic = argv[2];\n      g_content = argv[3];\n\n      string name = ProcessInfo::username()+\"@\"+ProcessInfo::hostname();\n      name += \":\" + ProcessInfo::pidString();\n\n      if (g_content == \"-\")\n      {\n        EventLoopThread loopThread;\n        g_loop = loopThread.startLoop();\n        PubSubClient client(g_loop, InetAddress(hostip, port), name);\n        client.start();\n\n        string line;\n        while (getline(std::cin, line))\n        {\n          client.publish(g_topic, line);\n        }\n        client.stop();\n        CurrentThread::sleepUsec(1000*1000);\n      }\n      else\n      {\n        EventLoop loop;\n        g_loop = &loop;\n        PubSubClient client(g_loop, InetAddress(hostip, port), name);\n        client.setConnectionCallback(connection);\n        client.start();\n        loop.loop();\n      }\n    }\n    else\n    {\n      printf(\"Usage: %s hub_ip:port topic content\\n\", argv[0]);\n    }\n  }\n  else\n  {\n    printf(\"Usage: %s hub_ip:port topic content\\n\"\n           \"Read contents from stdin:\\n\"\n           \"  %s hub_ip:port topic -\\n\", argv[0], argv[0]);\n  }\n}\n"
  },
  {
    "path": "examples/hub/pubsub.cc",
    "content": "#include \"examples/hub/pubsub.h\"\n#include \"examples/hub/codec.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace pubsub;\n\nPubSubClient::PubSubClient(EventLoop* loop,\n                           const InetAddress& hubAddr,\n                           const string& name)\n  : client_(loop, hubAddr, name)\n{\n  // FIXME: dtor is not thread safe\n  client_.setConnectionCallback(\n      std::bind(&PubSubClient::onConnection, this, _1));\n  client_.setMessageCallback(\n      std::bind(&PubSubClient::onMessage, this, _1, _2, _3));\n}\n\nvoid PubSubClient::start()\n{\n  client_.connect();\n}\n\nvoid PubSubClient::stop()\n{\n  client_.disconnect();\n}\n\nbool PubSubClient::connected() const\n{\n  return conn_ && conn_->connected();\n}\n\nbool PubSubClient::subscribe(const string& topic, const SubscribeCallback& cb)\n{\n  string message = \"sub \" + topic + \"\\r\\n\";\n  subscribeCallback_ = cb;\n  return send(message);\n}\n\nvoid PubSubClient::unsubscribe(const string& topic)\n{\n  string message = \"unsub \" + topic + \"\\r\\n\";\n  send(message);\n}\n\n\nbool PubSubClient::publish(const string& topic, const string& content)\n{\n  string message = \"pub \" + topic + \"\\r\\n\" + content + \"\\r\\n\";\n  return send(message);\n}\n\nvoid PubSubClient::onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn_ = conn;\n    // FIXME: re-sub\n  }\n  else\n  {\n    conn_.reset();\n  }\n  if (connectionCallback_)\n  {\n    connectionCallback_(this);\n  }\n}\n\nvoid PubSubClient::onMessage(const TcpConnectionPtr& conn,\n                             Buffer* buf,\n                             Timestamp receiveTime)\n{\n  ParseResult result = kSuccess;\n  while (result == kSuccess)\n  {\n    string cmd;\n    string topic;\n    string content;\n    result = parseMessage(buf, &cmd, &topic, &content);\n    if (result == kSuccess)\n    {\n      if (cmd == \"pub\" && subscribeCallback_)\n      {\n        subscribeCallback_(topic, content, receiveTime);\n      }\n    }\n    else if (result == kError)\n    {\n      conn->shutdown();\n    }\n  }\n}\n\nbool PubSubClient::send(const string& message)\n{\n  bool succeed = false;\n  if (conn_ && conn_->connected())\n  {\n    conn_->send(message);\n    succeed = true;\n  }\n  return succeed;\n}\n"
  },
  {
    "path": "examples/hub/pubsub.h",
    "content": "#ifndef MUDUO_EXAMPLES_HUB_PUBSUB_H\n#define MUDUO_EXAMPLES_HUB_PUBSUB_H\n\n#include \"muduo/net/TcpClient.h\"\n\nnamespace pubsub\n{\nusing muduo::string;\n\n// FIXME: dtor is not thread safe\nclass PubSubClient : muduo::noncopyable\n{\n public:\n  typedef std::function<void (PubSubClient*)> ConnectionCallback;\n  typedef std::function<void (const string& topic,\n                              const string& content,\n                              muduo::Timestamp)> SubscribeCallback;\n\n  PubSubClient(muduo::net::EventLoop* loop,\n               const muduo::net::InetAddress& hubAddr,\n               const string& name);\n  void start();\n  void stop();\n  bool connected() const;\n\n  void setConnectionCallback(const ConnectionCallback& cb)\n  { connectionCallback_ = cb; }\n\n  bool subscribe(const string& topic, const SubscribeCallback& cb);\n  void unsubscribe(const string& topic);\n  bool publish(const string& topic, const string& content);\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp receiveTime);\n  bool send(const string& message);\n\n  muduo::net::TcpClient client_;\n  muduo::net::TcpConnectionPtr conn_;\n  ConnectionCallback connectionCallback_;\n  SubscribeCallback subscribeCallback_;\n};\n}  // namespace pubsub\n\n#endif  // MUDUO_EXAMPLES_HUB_PUBSUB_H\n"
  },
  {
    "path": "examples/hub/sub.cc",
    "content": "#include \"examples/hub/pubsub.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <vector>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace pubsub;\n\nEventLoop* g_loop = NULL;\nstd::vector<string> g_topics;\n\nvoid subscription(const string& topic, const string& content, Timestamp)\n{\n  printf(\"%s: %s\\n\", topic.c_str(), content.c_str());\n}\n\nvoid connection(PubSubClient* client)\n{\n  if (client->connected())\n  {\n    for (std::vector<string>::iterator it = g_topics.begin();\n        it != g_topics.end(); ++it)\n    {\n      client->subscribe(*it, subscription);\n    }\n  }\n  else\n  {\n    g_loop->quit();\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc > 2)\n  {\n    string hostport = argv[1];\n    size_t colon = hostport.find(':');\n    if (colon != string::npos)\n    {\n      string hostip = hostport.substr(0, colon);\n      uint16_t port = static_cast<uint16_t>(atoi(hostport.c_str()+colon+1));\n      for (int i = 2; i < argc; ++i)\n      {\n        g_topics.push_back(argv[i]);\n      }\n\n      EventLoop loop;\n      g_loop = &loop;\n      string name = ProcessInfo::username()+\"@\"+ProcessInfo::hostname();\n      name += \":\" + ProcessInfo::pidString();\n      PubSubClient client(&loop, InetAddress(hostip, port), name);\n      client.setConnectionCallback(connection);\n      client.start();\n      loop.loop();\n    }\n    else\n    {\n      printf(\"Usage: %s hub_ip:port topic [topic ...]\\n\", argv[0]);\n    }\n  }\n  else\n  {\n    printf(\"Usage: %s hub_ip:port topic [topic ...]\\n\", argv[0]);\n  }\n}\n"
  },
  {
    "path": "examples/idleconnection/CMakeLists.txt",
    "content": "add_executable(idleconnection_echo echo.cc main.cc)\ntarget_link_libraries(idleconnection_echo muduo_net)\n\nadd_executable(idleconnection_echo2 sortedlist.cc)\ntarget_link_libraries(idleconnection_echo2 muduo_net)\n"
  },
  {
    "path": "examples/idleconnection/echo.cc",
    "content": "#include \"examples/idleconnection/echo.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n\nEchoServer::EchoServer(EventLoop* loop,\n                       const InetAddress& listenAddr,\n                       int idleSeconds)\n  : server_(loop, listenAddr, \"EchoServer\"),\n    connectionBuckets_(idleSeconds)\n{\n  server_.setConnectionCallback(\n      std::bind(&EchoServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n  loop->runEvery(1.0, std::bind(&EchoServer::onTimer, this));\n  connectionBuckets_.resize(idleSeconds);\n  dumpConnectionBuckets();\n}\n\nvoid EchoServer::start()\n{\n  server_.start();\n}\n\nvoid EchoServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"EchoServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n\n  if (conn->connected())\n  {\n    EntryPtr entry(new Entry(conn));\n    connectionBuckets_.back().insert(entry);\n    dumpConnectionBuckets();\n    WeakEntryPtr weakEntry(entry);\n    conn->setContext(weakEntry);\n  }\n  else\n  {\n    assert(!conn->getContext().empty());\n    WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));\n    LOG_DEBUG << \"Entry use_count = \" << weakEntry.use_count();\n  }\n}\n\nvoid EchoServer::onMessage(const TcpConnectionPtr& conn,\n                           Buffer* buf,\n                           Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" echo \" << msg.size()\n           << \" bytes at \" << time.toString();\n  conn->send(msg);\n\n  assert(!conn->getContext().empty());\n  WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));\n  EntryPtr entry(weakEntry.lock());\n  if (entry)\n  {\n    connectionBuckets_.back().insert(entry);\n    dumpConnectionBuckets();\n  }\n}\n\nvoid EchoServer::onTimer()\n{\n  connectionBuckets_.push_back(Bucket());\n  dumpConnectionBuckets();\n}\n\nvoid EchoServer::dumpConnectionBuckets() const\n{\n  LOG_INFO << \"size = \" << connectionBuckets_.size();\n  int idx = 0;\n  for (WeakConnectionList::const_iterator bucketI = connectionBuckets_.begin();\n      bucketI != connectionBuckets_.end();\n      ++bucketI, ++idx)\n  {\n    const Bucket& bucket = *bucketI;\n    printf(\"[%d] len = %zd : \", idx, bucket.size());\n    for (const auto& it : bucket)\n    {\n      bool connectionDead = it->weakConn_.expired();\n      printf(\"%p(%ld)%s, \", get_pointer(it), it.use_count(),\n          connectionDead ? \" DEAD\" : \"\");\n    }\n    puts(\"\");\n  }\n}\n\n"
  },
  {
    "path": "examples/idleconnection/echo.h",
    "content": "#ifndef MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H\n#define MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H\n\n#include \"muduo/net/TcpServer.h\"\n//#include <muduo/base/Types.h>\n\n#include <unordered_set>\n\n#include <boost/circular_buffer.hpp>\n\n// RFC 862\nclass EchoServer\n{\n public:\n  EchoServer(muduo::net::EventLoop* loop,\n             const muduo::net::InetAddress& listenAddr,\n             int idleSeconds);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  void onTimer();\n\n  void dumpConnectionBuckets() const;\n\n  typedef std::weak_ptr<muduo::net::TcpConnection> WeakTcpConnectionPtr;\n\n  struct Entry : public muduo::copyable\n  {\n    explicit Entry(const WeakTcpConnectionPtr& weakConn)\n      : weakConn_(weakConn)\n    {\n    }\n\n    ~Entry()\n    {\n      muduo::net::TcpConnectionPtr conn = weakConn_.lock();\n      if (conn)\n      {\n        conn->shutdown();\n      }\n    }\n\n    WeakTcpConnectionPtr weakConn_;\n  };\n  typedef std::shared_ptr<Entry> EntryPtr;\n  typedef std::weak_ptr<Entry> WeakEntryPtr;\n  typedef std::unordered_set<EntryPtr> Bucket;\n  typedef boost::circular_buffer<Bucket> WeakConnectionList;\n\n  muduo::net::TcpServer server_;\n  WeakConnectionList connectionBuckets_;\n};\n\n#endif  // MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H\n"
  },
  {
    "path": "examples/idleconnection/main.cc",
    "content": "#include \"examples/idleconnection/echo.h\"\n#include <stdio.h>\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n/*\nvoid testHash()\n{\n  boost::hash<std::shared_ptr<int> > h;\n  std::shared_ptr<int> x1(new int(10));\n  std::shared_ptr<int> x2(new int(10));\n  h(x1);\n  assert(h(x1) != h(x2));\n  x1 = x2;\n  assert(h(x1) == h(x2));\n  x1.reset();\n  assert(h(x1) != h(x2));\n  x2.reset();\n  assert(h(x1) == h(x2));\n}\n*/\n\nint main(int argc, char* argv[])\n{\n  // testHash();\n  EventLoop loop;\n  InetAddress listenAddr(2007);\n  int idleSeconds = 10;\n  if (argc > 1)\n  {\n    idleSeconds = atoi(argv[1]);\n  }\n  LOG_INFO << \"pid = \" << getpid() << \", idle seconds = \" << idleSeconds;\n  EchoServer server(&loop, listenAddr, idleSeconds);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/idleconnection/sortedlist.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n#include <list>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n// RFC 862\nclass EchoServer\n{\n public:\n  EchoServer(EventLoop* loop,\n             const InetAddress& listenAddr,\n             int idleSeconds);\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn);\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp time);\n\n  void onTimer();\n\n  void dumpConnectionList() const;\n\n  typedef std::weak_ptr<TcpConnection> WeakTcpConnectionPtr;\n  typedef std::list<WeakTcpConnectionPtr> WeakConnectionList;\n\n  struct Node : public muduo::copyable\n  {\n    Timestamp lastReceiveTime;\n    WeakConnectionList::iterator position;\n  };\n\n  TcpServer server_;\n  int idleSeconds_;\n  WeakConnectionList connectionList_;\n};\n\nEchoServer::EchoServer(EventLoop* loop,\n                       const InetAddress& listenAddr,\n                       int idleSeconds)\n  : server_(loop, listenAddr, \"EchoServer\"),\n    idleSeconds_(idleSeconds)\n{\n  server_.setConnectionCallback(\n      std::bind(&EchoServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n  loop->runEvery(1.0, std::bind(&EchoServer::onTimer, this));\n  dumpConnectionList();\n}\n\nvoid EchoServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"EchoServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n\n  if (conn->connected())\n  {\n    Node node;\n    node.lastReceiveTime = Timestamp::now();\n    connectionList_.push_back(conn);\n    node.position = --connectionList_.end();\n    conn->setContext(node);\n  }\n  else\n  {\n    assert(!conn->getContext().empty());\n    const Node& node = boost::any_cast<const Node&>(conn->getContext());\n    connectionList_.erase(node.position);\n  }\n  dumpConnectionList();\n}\n\nvoid EchoServer::onMessage(const TcpConnectionPtr& conn,\n                           Buffer* buf,\n                           Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" echo \" << msg.size()\n           << \" bytes at \" << time.toString();\n  conn->send(msg);\n\n  assert(!conn->getContext().empty());\n  Node* node = boost::any_cast<Node>(conn->getMutableContext());\n  node->lastReceiveTime = time;\n  connectionList_.splice(connectionList_.end(), connectionList_, node->position);\n  assert(node->position == --connectionList_.end());\n\n  dumpConnectionList();\n}\n\nvoid EchoServer::onTimer()\n{\n  dumpConnectionList();\n  Timestamp now = Timestamp::now();\n  for (WeakConnectionList::iterator it = connectionList_.begin();\n      it != connectionList_.end();)\n  {\n    TcpConnectionPtr conn = it->lock();\n    if (conn)\n    {\n      Node* n = boost::any_cast<Node>(conn->getMutableContext());\n      double age = timeDifference(now, n->lastReceiveTime);\n      if (age > idleSeconds_)\n      {\n        if (conn->connected())\n        {\n          conn->shutdown();\n          LOG_INFO << \"shutting down \" << conn->name();\n          conn->forceCloseWithDelay(3.5);  // > round trip of the whole Internet.\n        }\n      }\n      else if (age < 0)\n      {\n        LOG_WARN << \"Time jump\";\n        n->lastReceiveTime = now;\n      }\n      else\n      {\n        break;\n      }\n      ++it;\n    }\n    else\n    {\n      LOG_WARN << \"Expired\";\n      it = connectionList_.erase(it);\n    }\n  }\n}\n\nvoid EchoServer::dumpConnectionList() const\n{\n  LOG_INFO << \"size = \" << connectionList_.size();\n\n  for (WeakConnectionList::const_iterator it = connectionList_.begin();\n      it != connectionList_.end(); ++it)\n  {\n    TcpConnectionPtr conn = it->lock();\n    if (conn)\n    {\n      printf(\"conn %p\\n\", get_pointer(conn));\n      const Node& n = boost::any_cast<const Node&>(conn->getContext());\n      printf(\"    time %s\\n\", n.lastReceiveTime.toString().c_str());\n    }\n    else\n    {\n      printf(\"expired\\n\");\n    }\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  InetAddress listenAddr(2007);\n  int idleSeconds = 10;\n  if (argc > 1)\n  {\n    idleSeconds = atoi(argv[1]);\n  }\n  LOG_INFO << \"pid = \" << getpid() << \", idle seconds = \" << idleSeconds;\n  EchoServer server(&loop, listenAddr, idleSeconds);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/maxconnection/CMakeLists.txt",
    "content": "add_executable(maxconnection_echo echo.cc main.cc)\ntarget_link_libraries(maxconnection_echo muduo_net)\n"
  },
  {
    "path": "examples/maxconnection/echo.cc",
    "content": "#include \"examples/maxconnection/echo.h\"\n\n#include \"muduo/base/Logging.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEchoServer::EchoServer(EventLoop* loop,\n                       const InetAddress& listenAddr,\n                       int maxConnections)\n  : server_(loop, listenAddr, \"EchoServer\"),\n    numConnected_(0),\n    kMaxConnections_(maxConnections)\n{\n  server_.setConnectionCallback(\n      std::bind(&EchoServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n}\n\nvoid EchoServer::start()\n{\n  server_.start();\n}\n\nvoid EchoServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"EchoServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n\n  if (conn->connected())\n  {\n    ++numConnected_;\n    if (numConnected_ > kMaxConnections_)\n    {\n      conn->shutdown();\n      conn->forceCloseWithDelay(3.0);  // > round trip of the whole Internet.\n    }\n  }\n  else\n  {\n    --numConnected_;\n  }\n  LOG_INFO << \"numConnected = \" << numConnected_;\n}\n\nvoid EchoServer::onMessage(const TcpConnectionPtr& conn,\n                           Buffer* buf,\n                           Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" echo \" << msg.size() << \" bytes at \" << time.toString();\n  conn->send(msg);\n}\n\n"
  },
  {
    "path": "examples/maxconnection/echo.h",
    "content": "#ifndef MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H\n#define MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 862\nclass EchoServer\n{\n public:\n  EchoServer(muduo::net::EventLoop* loop,\n             const muduo::net::InetAddress& listenAddr,\n             int maxConnections);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  muduo::net::TcpServer server_;\n  int numConnected_; // should be atomic_int\n  const int kMaxConnections_;\n};\n\n#endif  // MUDUO_EXAMPLES_MAXCONNECTION_ECHO_H\n"
  },
  {
    "path": "examples/maxconnection/main.cc",
    "content": "#include \"examples/maxconnection/echo.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2007);\n  int maxConnections = 5;\n  if (argc > 1)\n  {\n    maxConnections = atoi(argv[1]);\n  }\n  LOG_INFO << \"maxConnections = \" << maxConnections;\n  EchoServer server(&loop, listenAddr, maxConnections);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/memcached/README",
    "content": "Simple implementation of memcached protocol for both server and client side.\nNot meant to replace memcached, but just sample code of network programming with muduo.\n\nServer limits:\n - The memory management is not customized, just uses (tc)malloc.\n - It doesn't control the memory footprint\n - Unix domain socket is not supported\n - Only listen on one TCP port\n\nServer goals:\n - Pass as many feature tests as possible\n - Prefer simplicity over performance\n\nTODO:\n - incr/decr\n - UDP\n - Binary protocol\n - expiration\n - LRU\n"
  },
  {
    "path": "examples/memcached/client/CMakeLists.txt",
    "content": "if(BOOSTPO_LIBRARY)\n  add_executable(memcached_bench bench.cc)\n  target_link_libraries(memcached_bench muduo_net boost_program_options)\nendif()\n"
  },
  {
    "path": "examples/memcached/client/bench.cc",
    "content": "#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <boost/program_options.hpp>\n#include <iostream>\n\n#include <stdio.h>\n\nnamespace po = boost::program_options;\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass Client : noncopyable\n{\n public:\n  enum Operation\n  {\n    kGet,\n    kSet,\n  };\n\n  Client(const string& name,\n         EventLoop* loop,\n         const InetAddress& serverAddr,\n         Operation op,\n         int requests,\n         int keys,\n         int valuelen,\n         CountDownLatch* connected,\n         CountDownLatch* finished)\n    : name_(name),\n      client_(loop, serverAddr, name),\n      op_(op),\n      sent_(0),\n      acked_(0),\n      requests_(requests),\n      keys_(keys),\n      valuelen_(valuelen),\n      value_(valuelen_, 'a'),\n      connected_(connected),\n      finished_(finished)\n  {\n    value_ += \"\\r\\n\";\n    client_.setConnectionCallback(std::bind(&Client::onConnection, this, _1));\n    client_.setMessageCallback(std::bind(&Client::onMessage, this, _1, _2, _3));\n    client_.connect();\n  }\n\n  void send()\n  {\n    Buffer buf;\n    fill(&buf);\n    conn_->send(&buf);\n  }\n\n private:\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      conn_ = conn;\n      connected_->countDown();\n    }\n    else\n    {\n      conn_.reset();\n      client_.getLoop()->queueInLoop(std::bind(&CountDownLatch::countDown, finished_));\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buffer,\n                 Timestamp receiveTime)\n  {\n    if (op_ == kSet)\n    {\n      while (buffer->readableBytes() > 0)\n      {\n        const char* crlf = buffer->findCRLF();\n        if (crlf)\n        {\n          buffer->retrieveUntil(crlf+2);\n          ++acked_;\n          if (sent_ < requests_)\n          {\n            send();\n          }\n        }\n        else\n        {\n          break;\n        }\n      }\n    }\n    else\n    {\n      while (buffer->readableBytes() > 0)\n      {\n        const char* end = static_cast<const char*>(memmem(buffer->peek(),\n                                                          buffer->readableBytes(),\n                                                          \"END\\r\\n\", 5));\n        if (end)\n        {\n          buffer->retrieveUntil(end+5);\n          ++acked_;\n          if (sent_ < requests_)\n          {\n            send();\n          }\n        }\n        else\n        {\n          break;\n        }\n      }\n    }\n    if (acked_ == requests_)\n    {\n      conn_->shutdown();\n    }\n  }\n\n  void fill(Buffer* buf)\n  {\n    char req[256];\n    if (op_ == kSet)\n    {\n      snprintf(req, sizeof req, \"set %s%d 42 0 %d\\r\\n\", name_.c_str(), sent_ % keys_, valuelen_);\n      ++sent_;\n      buf->append(req);\n      buf->append(value_);\n    }\n    else\n    {\n      snprintf(req, sizeof req, \"get %s%d\\r\\n\", name_.c_str(), sent_ % keys_);\n      ++sent_;\n      buf->append(req);\n    }\n  }\n\n  string name_;\n  TcpClient client_;\n  TcpConnectionPtr conn_;\n  const Operation op_;\n  int sent_;\n  int acked_;\n  const int requests_;\n  const int keys_;\n  const int valuelen_;\n  string value_;\n  CountDownLatch* const connected_;\n  CountDownLatch* const finished_;\n};\n\nint main(int argc, char* argv[])\n{\n  Logger::setLogLevel(Logger::WARN);\n\n  uint16_t tcpport = 11211;\n  string hostIp = \"127.0.0.1\";\n  int threads = 4;\n  int clients = 100;\n  int requests = 100000;\n  int keys = 10000;\n  bool set = false;\n\n  po::options_description desc(\"Allowed options\");\n  desc.add_options()\n      (\"help,h\", \"Help\")\n      (\"port,p\", po::value<uint16_t>(&tcpport), \"TCP port\")\n      (\"ip,i\", po::value<string>(&hostIp), \"Host IP\")\n      (\"threads,t\", po::value<int>(&threads), \"Number of worker threads\")\n      (\"clients,c\", po::value<int>(&clients), \"Number of concurrent clients\")\n      (\"requests,r\", po::value<int>(&requests), \"Number of requests per clients\")\n      (\"keys,k\", po::value<int>(&keys), \"Number of keys per clients\")\n      (\"set,s\", \"Get or Set\")\n      ;\n\n  po::variables_map vm;\n  po::store(po::parse_command_line(argc, argv, desc), vm);\n  po::notify(vm);\n\n  if (vm.count(\"help\"))\n  {\n    std::cout << desc << \"\\n\";\n    return 0;\n  }\n  set = vm.count(\"set\");\n\n  InetAddress serverAddr(hostIp, tcpport);\n  LOG_WARN << \"Connecting \" << serverAddr.toIpPort();\n\n  EventLoop loop;\n  EventLoopThreadPool pool(&loop, \"bench-memcache\");\n\n  int valuelen = 100;\n  Client::Operation op = set ? Client::kSet : Client::kGet;\n\n  double memoryMiB = 1.0 * clients * keys * (32+80+valuelen+8) / 1024 / 1024;\n  LOG_WARN << \"estimated memcached-debug memory usage \" << int(memoryMiB) << \" MiB\";\n\n  pool.setThreadNum(threads);\n  pool.start();\n\n  char buf[32];\n  CountDownLatch connected(clients);\n  CountDownLatch finished(clients);\n  std::vector<std::unique_ptr<Client>> holder;\n  for (int i = 0; i < clients; ++i)\n  {\n    snprintf(buf, sizeof buf, \"%d-\", i+1);\n    holder.emplace_back(new Client(buf,\n                                pool.getNextLoop(),\n                                serverAddr,\n                                op,\n                                requests,\n                                keys,\n                                valuelen,\n                                &connected,\n                                &finished));\n  }\n  connected.wait();\n  LOG_WARN << clients << \" clients all connected\";\n  Timestamp start = Timestamp::now();\n  for (int i = 0; i < clients; ++i)\n  {\n    holder[i]->send();\n  }\n  finished.wait();\n  Timestamp end = Timestamp::now();\n  LOG_WARN << \"All finished\";\n  double seconds = timeDifference(end, start);\n  LOG_WARN << seconds << \" sec\";\n  LOG_WARN << 1.0 * clients * requests / seconds << \" QPS\";\n}\n"
  },
  {
    "path": "examples/memcached/server/CMakeLists.txt",
    "content": "if(BOOSTPO_LIBRARY)\n  add_executable(memcached_debug Item.cc MemcacheServer.cc Session.cc server.cc)\n  target_link_libraries(memcached_debug muduo_net muduo_inspect boost_program_options)\nendif()\n\nadd_executable(memcached_footprint Item.cc MemcacheServer.cc Session.cc footprint_test.cc)\ntarget_link_libraries(memcached_footprint muduo_net muduo_inspect)\n\nif(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY)\n  set_target_properties(memcached_footprint PROPERTIES COMPILE_FLAGS \"-DHAVE_TCMALLOC\")\n  if(BOOSTPO_LIBRARY)\n    set_target_properties(memcached_debug PROPERTIES COMPILE_FLAGS \"-DHAVE_TCMALLOC\")\n  endif()\nendif()\n\n"
  },
  {
    "path": "examples/memcached/server/Item.cc",
    "content": "#include \"examples/memcached/server/Item.h\"\n\n#include \"muduo/base/LogStream.h\"\n#include \"muduo/net/Buffer.h\"\n\n#include <boost/functional/hash/hash.hpp>\n\n#include <string.h> // memcpy\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nItem::Item(StringPiece keyArg,\n           uint32_t flagsArg,\n           int exptimeArg,\n           int valuelen,\n           uint64_t casArg)\n  : keylen_(keyArg.size()),\n    flags_(flagsArg),\n    rel_exptime_(exptimeArg),\n    valuelen_(valuelen),\n    receivedBytes_(0),\n    cas_(casArg),\n    hash_(boost::hash_range(keyArg.begin(), keyArg.end())),\n    data_(static_cast<char*>(::malloc(totalLen())))\n{\n  assert(valuelen_ >= 2);\n  assert(receivedBytes_ < totalLen());\n  append(keyArg.data(), keylen_);\n}\n\nvoid Item::append(const char* data, size_t len)\n{\n  assert(len <= neededBytes());\n  memcpy(data_ + receivedBytes_, data, len);\n  receivedBytes_ += static_cast<int>(len);\n  assert(receivedBytes_ <= totalLen());\n}\n\nvoid Item::output(Buffer* out, bool needCas) const\n{\n  out->append(\"VALUE \");\n  out->append(data_, keylen_);\n  LogStream buf;\n  buf << ' ' << flags_ << ' ' << valuelen_-2;\n  if (needCas)\n  {\n    buf << ' ' << cas_;\n  }\n  buf << \"\\r\\n\";\n  out->append(buf.buffer().data(), buf.buffer().length());\n  out->append(value(), valuelen_);\n}\n\nvoid Item::resetKey(StringPiece k)\n{\n  assert(k.size() <= 250);\n  keylen_ = k.size();\n  receivedBytes_ = 0;\n  append(k.data(), k.size());\n  hash_ = boost::hash_range(k.begin(), k.end());\n}\n"
  },
  {
    "path": "examples/memcached/server/Item.h",
    "content": "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H\n#define MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n\n#include <memory>\n\nnamespace muduo\n{\nnamespace net\n{\nclass Buffer;\n}\n}\n\nclass Item;\ntypedef std::shared_ptr<Item> ItemPtr;  // TODO: use unique_ptr\ntypedef std::shared_ptr<const Item> ConstItemPtr;  // TODO: use unique_ptr\n\n// Item is immutable once added into hash table\nclass Item : muduo::noncopyable\n{\n public:\n  enum UpdatePolicy\n  {\n    kInvalid,\n    kSet,\n    kAdd,\n    kReplace,\n    kAppend,\n    kPrepend,\n    kCas,\n  };\n\n  static ItemPtr makeItem(muduo::StringPiece keyArg,\n                          uint32_t flagsArg,\n                          int exptimeArg,\n                          int valuelen,\n                          uint64_t casArg)\n  {\n    return std::make_shared<Item>(keyArg, flagsArg, exptimeArg, valuelen, casArg);\n    //return ItemPtr(new Item(keyArg, flagsArg, exptimeArg, valuelen, casArg));\n  }\n\n  Item(muduo::StringPiece keyArg,\n       uint32_t flagsArg,\n       int exptimeArg,\n       int valuelen,\n       uint64_t casArg);\n\n  ~Item()\n  {\n    ::free(data_);\n  }\n\n  muduo::StringPiece key() const\n  {\n    return muduo::StringPiece(data_, keylen_);\n  }\n\n  uint32_t flags() const\n  {\n    return flags_;\n  }\n\n  int rel_exptime() const\n  {\n    return rel_exptime_;\n  }\n\n  const char* value() const\n  {\n    return data_+keylen_;\n  }\n\n  size_t valueLength() const\n  {\n    return valuelen_;\n  }\n\n  uint64_t cas() const\n  {\n    return cas_;\n  }\n\n  size_t hash() const\n  {\n    return hash_;\n  }\n\n  void setCas(uint64_t casArg)\n  {\n    cas_ = casArg;\n  }\n\n  size_t neededBytes() const\n  {\n    return totalLen() - receivedBytes_;\n  }\n\n  void append(const char* data, size_t len);\n\n  bool endsWithCRLF() const\n  {\n    return receivedBytes_ == totalLen()\n        && data_[totalLen()-2] == '\\r'\n        && data_[totalLen()-1] == '\\n';\n  }\n\n  void output(muduo::net::Buffer* out, bool needCas = false) const;\n\n  void resetKey(muduo::StringPiece k);\n\n private:\n  int totalLen() const { return keylen_ + valuelen_; }\n\n  int            keylen_;\n  const uint32_t flags_;\n  const int      rel_exptime_;\n  const int      valuelen_;\n  int            receivedBytes_;  // FIXME: remove this member\n  uint64_t       cas_;\n  size_t         hash_;\n  char*          data_;\n};\n\n#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_ITEM_H\n"
  },
  {
    "path": "examples/memcached/server/MemcacheServer.cc",
    "content": "#include \"examples/memcached/server/MemcacheServer.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nmuduo::AtomicInt64 g_cas;\n\nMemcacheServer::Options::Options()\n{\n  memZero(this, sizeof(*this));\n}\n\nstruct MemcacheServer::Stats\n{\n};\n\nMemcacheServer::MemcacheServer(muduo::net::EventLoop* loop, const Options& options)\n  : loop_(loop),\n    options_(options),\n    startTime_(::time(NULL)-1),\n    server_(loop, InetAddress(options.tcpport), \"muduo-memcached\"),\n    stats_(new Stats)\n{\n  server_.setConnectionCallback(\n      std::bind(&MemcacheServer::onConnection, this, _1));\n}\n\nMemcacheServer::~MemcacheServer() = default;\n\nvoid MemcacheServer::start()\n{\n  server_.start();\n}\n\nvoid MemcacheServer::stop()\n{\n  loop_->runAfter(3.0, std::bind(&EventLoop::quit, loop_));\n}\n\nbool MemcacheServer::storeItem(const ItemPtr& item, const Item::UpdatePolicy policy, bool* exists)\n{\n  assert(item->neededBytes() == 0);\n  MutexLock& mutex = shards_[item->hash() % kShards].mutex;\n  ItemMap& items = shards_[item->hash() % kShards].items;\n  MutexLockGuard lock(mutex);\n  ItemMap::const_iterator it = items.find(item);\n  *exists = it != items.end();\n  if (policy == Item::kSet)\n  {\n    item->setCas(g_cas.incrementAndGet());\n    if (*exists)\n    {\n      items.erase(it);\n    }\n    items.insert(item);\n  }\n  else\n  {\n    if (policy == Item::kAdd)\n    {\n      if (*exists)\n      {\n        return false;\n      }\n      else\n      {\n        item->setCas(g_cas.incrementAndGet());\n        items.insert(item);\n      }\n    }\n    else if (policy == Item::kReplace)\n    {\n      if (*exists)\n      {\n        item->setCas(g_cas.incrementAndGet());\n        items.erase(it);\n        items.insert(item);\n      }\n      else\n      {\n        return false;\n      }\n    }\n    else if (policy == Item::kAppend || policy == Item::kPrepend)\n    {\n      if (*exists)\n      {\n        const ConstItemPtr& oldItem = *it;\n        int newLen = static_cast<int>(item->valueLength() + oldItem->valueLength() - 2);\n        ItemPtr newItem(Item::makeItem(item->key(),\n                                       oldItem->flags(),\n                                       oldItem->rel_exptime(),\n                                       newLen,\n                                       g_cas.incrementAndGet()));\n        if (policy == Item::kAppend)\n        {\n          newItem->append(oldItem->value(), oldItem->valueLength() - 2);\n          newItem->append(item->value(), item->valueLength());\n        }\n        else\n        {\n          newItem->append(item->value(), item->valueLength() - 2);\n          newItem->append(oldItem->value(), oldItem->valueLength());\n        }\n        assert(newItem->neededBytes() == 0);\n        assert(newItem->endsWithCRLF());\n        items.erase(it);\n        items.insert(newItem);\n      }\n      else\n      {\n        return false;\n      }\n    }\n    else if (policy == Item::kCas)\n    {\n      if (*exists && (*it)->cas() == item->cas())\n      {\n        item->setCas(g_cas.incrementAndGet());\n        items.erase(it);\n        items.insert(item);\n      }\n      else\n      {\n        return false;\n      }\n    }\n    else\n    {\n      assert(false);\n    }\n  }\n  return true;\n}\n\nConstItemPtr MemcacheServer::getItem(const ConstItemPtr& key) const\n{\n  MutexLock& mutex = shards_[key->hash() % kShards].mutex;\n  const ItemMap& items = shards_[key->hash() % kShards].items;\n  MutexLockGuard lock(mutex);\n  ItemMap::const_iterator it = items.find(key);\n  return it != items.end() ? *it : ConstItemPtr();\n}\n\nbool MemcacheServer::deleteItem(const ConstItemPtr& key)\n{\n  MutexLock& mutex = shards_[key->hash() % kShards].mutex;\n  ItemMap& items = shards_[key->hash() % kShards].items;\n  MutexLockGuard lock(mutex);\n  return items.erase(key) == 1;\n}\n\nvoid MemcacheServer::onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    SessionPtr session(new Session(this, conn));\n    MutexLockGuard lock(mutex_);\n    assert(sessions_.find(conn->name()) == sessions_.end());\n    sessions_[conn->name()] = session;\n    // assert(sessions_.size() == stats_.current_conns);\n  }\n  else\n  {\n    MutexLockGuard lock(mutex_);\n    assert(sessions_.find(conn->name()) != sessions_.end());\n    sessions_.erase(conn->name());\n    // assert(sessions_.size() == stats_.current_conns);\n  }\n}\n"
  },
  {
    "path": "examples/memcached/server/MemcacheServer.h",
    "content": "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H\n#define MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H\n\n#include \"examples/memcached/server/Item.h\"\n#include \"examples/memcached/server/Session.h\"\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"examples/wordcount/hash.h\"\n\n#include <array>\n#include <unordered_map>\n#include <unordered_set>\n\nclass MemcacheServer : muduo::noncopyable\n{\n public:\n  struct Options\n  {\n    Options();\n    uint16_t tcpport;\n    uint16_t udpport;\n    uint16_t gperfport;\n    int threads;\n  };\n\n  MemcacheServer(muduo::net::EventLoop* loop, const Options&);\n  ~MemcacheServer();\n\n  void setThreadNum(int threads) { server_.setThreadNum(threads); }\n  void start();\n  void stop();\n\n  time_t startTime() const { return startTime_; }\n\n  bool storeItem(const ItemPtr& item, Item::UpdatePolicy policy, bool* exists);\n  ConstItemPtr getItem(const ConstItemPtr& key) const;\n  bool deleteItem(const ConstItemPtr& key);\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  struct Stats;\n\n  muduo::net::EventLoop* loop_;  // not own\n  Options options_;\n  const time_t startTime_;\n\n  mutable muduo::MutexLock mutex_;\n  std::unordered_map<string, SessionPtr> sessions_ GUARDED_BY(mutex_);\n\n  // a complicated solution to save memory\n  struct Hash\n  {\n    size_t operator()(const ConstItemPtr& x) const\n    {\n      return x->hash();\n    }\n  };\n\n  struct Equal\n  {\n    bool operator()(const ConstItemPtr& x, const ConstItemPtr& y) const\n    {\n      return x->key() == y->key();\n    }\n  };\n\n  typedef std::unordered_set<ConstItemPtr, Hash, Equal> ItemMap;\n\n  struct MapWithLock\n  {\n    ItemMap items;\n    mutable muduo::MutexLock mutex;\n  };\n\n  const static int kShards = 4096;\n\n  std::array<MapWithLock, kShards> shards_;\n\n  // NOT guarded by mutex_, but here because server_ has to destructs before\n  // sessions_\n  muduo::net::TcpServer server_;\n  std::unique_ptr<Stats> stats_ PT_GUARDED_BY(mutex_);\n};\n\n#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_MEMCACHESERVER_H\n"
  },
  {
    "path": "examples/memcached/server/Session.cc",
    "content": "#include \"examples/memcached/server/Session.h\"\n#include \"examples/memcached/server/MemcacheServer.h\"\n\n#ifdef HAVE_TCMALLOC\n#include <gperftools/malloc_extension.h>\n#endif\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstatic bool isBinaryProtocol(uint8_t firstByte)\n{\n  return firstByte == 0x80;\n}\n\nconst int kLongestKeySize = 250;\nstring Session::kLongestKey(kLongestKeySize, 'x');\n\ntemplate <typename InputIterator, typename Token>\nbool Session::SpaceSeparator::operator()(InputIterator& next, InputIterator end, Token& tok)\n{\n  while (next != end && *next == ' ')\n    ++next;\n  if (next == end)\n  {\n    tok.clear();\n    return false;\n  }\n  InputIterator start(next);\n  const char* sp = static_cast<const char*>(memchr(start, ' ', end - start));\n  if (sp)\n  {\n    tok.set(start, static_cast<int>(sp - start));\n    next = sp;\n  }\n  else\n  {\n    tok.set(start, static_cast<int>(end - next));\n    next = end;\n  }\n  return true;\n}\n\nstruct Session::Reader\n{\n  Reader(Tokenizer::iterator& beg, Tokenizer::iterator end)\n      : first_(beg),\n        last_(end)\n  {\n  }\n\n  template<typename T>\n  bool read(T* val)\n  {\n    if (first_ == last_)\n      return false;\n    char* end = NULL;\n    uint64_t x = strtoull((*first_).data(), &end, 10);\n    if (end == (*first_).end())\n    {\n      *val = static_cast<T>(x);\n      ++first_;\n      return true;\n    }\n    return false;\n  }\n\n private:\n  Tokenizer::iterator first_;\n  Tokenizer::iterator last_;;\n};\n\nvoid Session::onMessage(const muduo::net::TcpConnectionPtr& conn,\n                        muduo::net::Buffer* buf,\n                        muduo::Timestamp)\n\n{\n  const size_t initialReadable = buf->readableBytes();\n\n  while (buf->readableBytes() > 0)\n  {\n    if (state_ == kNewCommand)\n    {\n      if (protocol_ == kAuto)\n      {\n        assert(bytesRead_ == 0);\n        protocol_ = isBinaryProtocol(buf->peek()[0]) ? kBinary : kAscii;\n      }\n\n      assert(protocol_ == kAscii || protocol_ == kBinary);\n      if (protocol_ == kBinary)\n      {\n        // FIXME\n      }\n      else  // ASCII protocol\n      {\n        const char* crlf = buf->findCRLF();\n        if (crlf)\n        {\n          int len = static_cast<int>(crlf - buf->peek());\n          StringPiece request(buf->peek(), len);\n          if (processRequest(request))\n          {\n            resetRequest();\n          }\n          buf->retrieveUntil(crlf + 2);\n        }\n        else\n        {\n          if (buf->readableBytes() > 1024)\n          {\n            // FIXME: check for 'get' and 'gets'\n            conn_->shutdown();\n            // buf->retrieveAll() ???\n          }\n          break;\n        }\n      }\n    }\n    else if (state_ == kReceiveValue)\n    {\n      receiveValue(buf);\n    }\n    else if (state_ == kDiscardValue)\n    {\n      discardValue(buf);\n    }\n    else\n    {\n      assert(false);\n    }\n  }\n  bytesRead_ += initialReadable - buf->readableBytes();\n}\n\nvoid Session::receiveValue(muduo::net::Buffer* buf)\n{\n  assert(currItem_.get());\n  assert(state_ == kReceiveValue);\n  // if (protocol_ == kBinary)\n\n  const size_t avail = std::min(buf->readableBytes(), currItem_->neededBytes());\n  assert(currItem_.unique());\n  currItem_->append(buf->peek(), avail);\n  buf->retrieve(avail);\n  if (currItem_->neededBytes() == 0)\n  {\n    if (currItem_->endsWithCRLF())\n    {\n      bool exists = false;\n      if (owner_->storeItem(currItem_, policy_, &exists))\n      {\n        reply(\"STORED\\r\\n\");\n      }\n      else\n      {\n        if (policy_ == Item::kCas)\n        {\n          if (exists)\n          {\n            reply(\"EXISTS\\r\\n\");\n          }\n          else\n          {\n            reply(\"NOT_FOUND\\r\\n\");\n          }\n        }\n        else\n        {\n          reply(\"NOT_STORED\\r\\n\");\n        }\n      }\n    }\n    else\n    {\n      reply(\"CLIENT_ERROR bad data chunk\\r\\n\");\n    }\n    resetRequest();\n    state_ = kNewCommand;\n  }\n}\n\nvoid Session::discardValue(muduo::net::Buffer* buf)\n{\n  assert(!currItem_);\n  assert(state_ == kDiscardValue);\n  if (buf->readableBytes() < bytesToDiscard_)\n  {\n    bytesToDiscard_ -= buf->readableBytes();\n    buf->retrieveAll();\n  }\n  else\n  {\n    buf->retrieve(bytesToDiscard_);\n    bytesToDiscard_ = 0;\n    resetRequest();\n    state_ = kNewCommand;\n  }\n}\n\nbool Session::processRequest(StringPiece request)\n{\n  assert(command_.empty());\n  assert(!noreply_);\n  assert(policy_ == Item::kInvalid);\n  assert(!currItem_);\n  assert(bytesToDiscard_ == 0);\n  ++requestsProcessed_;\n\n  // check 'noreply' at end of request line\n  if (request.size() >= 8)\n  {\n    StringPiece end(request.end() - 8, 8);\n    if (end == \" noreply\")\n    {\n      noreply_ = true;\n      request.remove_suffix(8);\n    }\n  }\n\n  SpaceSeparator sep;\n  Tokenizer tok(request.begin(), request.end(), sep);\n  Tokenizer::iterator beg = tok.begin();\n  if (beg == tok.end())\n  {\n    reply(\"ERROR\\r\\n\");\n    return true;\n  }\n  (*beg).CopyToString(&command_);\n  ++beg;\n  if (command_ == \"set\" || command_ == \"add\" || command_ == \"replace\"\n      || command_ == \"append\" || command_ == \"prepend\" || command_ == \"cas\")\n  {\n    // this normally returns false\n    return doUpdate(beg, tok.end());\n  }\n  else if (command_ == \"get\" || command_ == \"gets\")\n  {\n    bool cas = command_ == \"gets\";\n\n    // FIXME: send multiple chunks with write complete callback.\n    while (beg != tok.end())\n    {\n      StringPiece key = *beg;\n      bool good = key.size() <= kLongestKeySize;\n      if (!good)\n      {\n        reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n        return true;\n      }\n\n      needle_->resetKey(key);\n      ConstItemPtr item = owner_->getItem(needle_);\n      ++beg;\n      if (item)\n      {\n        item->output(&outputBuf_, cas);\n      }\n    }\n    outputBuf_.append(\"END\\r\\n\");\n\n    if (conn_->outputBuffer()->writableBytes() > 65536 + outputBuf_.readableBytes())\n    {\n      LOG_DEBUG << \"shrink output buffer from \" << conn_->outputBuffer()->internalCapacity();\n      conn_->outputBuffer()->shrink(65536 + outputBuf_.readableBytes());\n    }\n\n    conn_->send(&outputBuf_);\n  }\n  else if (command_ == \"delete\")\n  {\n    doDelete(beg, tok.end());\n  }\n  else if (command_ == \"version\")\n  {\n#ifdef HAVE_TCMALLOC\n    reply(\"VERSION 0.01 muduo with tcmalloc\\r\\n\");\n#else\n    reply(\"VERSION 0.01 muduo\\r\\n\");\n#endif\n  }\n#ifdef HAVE_TCMALLOC\n  else if (command_ == \"memstat\")\n  {\n    char buf[1024*64];\n    MallocExtension::instance()->GetStats(buf, sizeof buf);\n    reply(buf);\n  }\n#endif\n  else if (command_ == \"quit\")\n  {\n    conn_->shutdown();\n  }\n  else if (command_ == \"shutdown\")\n  {\n    // \"ERROR: shutdown not enabled\"\n    conn_->shutdown();\n    owner_->stop();\n  }\n  else\n  {\n    reply(\"ERROR\\r\\n\");\n    LOG_INFO << \"Unknown command: \" << command_;\n  }\n  return true;\n}\n\nvoid Session::resetRequest()\n{\n  command_.clear();\n  noreply_ = false;\n  policy_ = Item::kInvalid;\n  currItem_.reset();\n  bytesToDiscard_ = 0;\n}\n\nvoid Session::reply(muduo::StringPiece msg)\n{\n  if (!noreply_)\n  {\n    conn_->send(msg.data(), msg.size());\n  }\n}\n\nbool Session::doUpdate(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end)\n{\n  if (command_ == \"set\")\n    policy_ = Item::kSet;\n  else if (command_ == \"add\")\n    policy_ = Item::kAdd;\n  else if (command_ == \"replace\")\n    policy_ = Item::kReplace;\n  else if (command_ == \"append\")\n    policy_ = Item::kAppend;\n  else if (command_ == \"prepend\")\n    policy_ = Item::kPrepend;\n  else if (command_ == \"cas\")\n    policy_ = Item::kCas;\n  else\n    assert(false);\n\n  // FIXME: check (beg != end)\n  StringPiece key = (*beg);\n  ++beg;\n  bool good = key.size() <= kLongestKeySize;\n\n  uint32_t flags = 0;\n  time_t exptime = 1;\n  int bytes = -1;\n  uint64_t cas = 0;\n\n  Reader r(beg, end);\n  good = good && r.read(&flags) && r.read(&exptime) && r.read(&bytes);\n\n  int rel_exptime = static_cast<int>(exptime);\n  if (exptime > 60*60*24*30)\n  {\n    rel_exptime = static_cast<int>(exptime - owner_->startTime());\n    if (rel_exptime < 1)\n    {\n      rel_exptime = 1;\n    }\n  }\n  else\n  {\n    // rel_exptime = exptime + currentTime;\n  }\n\n  if (good && policy_ == Item::kCas)\n  {\n    good = r.read(&cas);\n  }\n\n  if (!good)\n  {\n    reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n    return true;\n  }\n  if (bytes > 1024*1024)\n  {\n    reply(\"SERVER_ERROR object too large for cache\\r\\n\");\n    needle_->resetKey(key);\n    owner_->deleteItem(needle_);\n    bytesToDiscard_ = bytes + 2;\n    state_ = kDiscardValue;\n    return false;\n  }\n  else\n  {\n    currItem_ = Item::makeItem(key, flags, rel_exptime, bytes + 2, cas);\n    state_ = kReceiveValue;\n    return false;\n  }\n}\n\nvoid Session::doDelete(Session::Tokenizer::iterator& beg, Session::Tokenizer::iterator end)\n{\n  assert(command_ == \"delete\");\n  // FIXME: check (beg != end)\n  StringPiece key = *beg;\n  bool good = key.size() <= kLongestKeySize;\n  ++beg;\n  if (!good)\n  {\n    reply(\"CLIENT_ERROR bad command line format\\r\\n\");\n  }\n  else if (beg != end && *beg != \"0\") // issue 108, old protocol\n  {\n    reply(\"CLIENT_ERROR bad command line format.  Usage: delete <key> [noreply]\\r\\n\");\n  }\n  else\n  {\n    needle_->resetKey(key);\n    if (owner_->deleteItem(needle_))\n    {\n      reply(\"DELETED\\r\\n\");\n    }\n    else\n    {\n      reply(\"NOT_FOUND\\r\\n\");\n    }\n  }\n}\n"
  },
  {
    "path": "examples/memcached/server/Session.h",
    "content": "#ifndef MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H\n#define MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H\n\n#include \"examples/memcached/server/Item.h\"\n\n#include \"muduo/base/Logging.h\"\n\n#include \"muduo/net/TcpConnection.h\"\n\n#include <boost/tokenizer.hpp>\n\nusing muduo::string;\n\nclass MemcacheServer;\n\nclass Session : public std::enable_shared_from_this<Session>,\n                muduo::noncopyable\n{\n public:\n  Session(MemcacheServer* owner, const muduo::net::TcpConnectionPtr& conn)\n    : owner_(owner),\n      conn_(conn),\n      state_(kNewCommand),\n      protocol_(kAscii), // FIXME\n      noreply_(false),\n      policy_(Item::kInvalid),\n      bytesToDiscard_(0),\n      needle_(Item::makeItem(kLongestKey, 0, 0, 2, 0)),\n      bytesRead_(0),\n      requestsProcessed_(0)\n  {\n    using std::placeholders::_1;\n    using std::placeholders::_2;\n    using std::placeholders::_3;\n\n    conn_->setMessageCallback(\n        std::bind(&Session::onMessage, this, _1, _2, _3));\n  }\n\n  ~Session()\n  {\n    LOG_INFO << \"requests processed: \" << requestsProcessed_\n             << \" input buffer size: \" << conn_->inputBuffer()->internalCapacity()\n             << \" output buffer size: \" << conn_->outputBuffer()->internalCapacity();\n  }\n\n private:\n  enum State\n  {\n    kNewCommand,\n    kReceiveValue,\n    kDiscardValue,\n  };\n\n  enum Protocol\n  {\n    kAscii,\n    kBinary,\n    kAuto,\n  };\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp);\n  void onWriteComplete(const muduo::net::TcpConnectionPtr& conn);\n  void receiveValue(muduo::net::Buffer* buf);\n  void discardValue(muduo::net::Buffer* buf);\n  // TODO: highWaterMark\n  // TODO: onWriteComplete\n\n  // returns true if finished a request\n  bool processRequest(muduo::StringPiece request);\n  void resetRequest();\n  void reply(muduo::StringPiece msg);\n\n  struct SpaceSeparator\n  {\n    void reset() {}\n    template <typename InputIterator, typename Token>\n    bool operator()(InputIterator& next, InputIterator end, Token& tok);\n  };\n\n  typedef boost::tokenizer<SpaceSeparator,\n      const char*,\n      muduo::StringPiece> Tokenizer;\n  struct Reader;\n  bool doUpdate(Tokenizer::iterator& beg, Tokenizer::iterator end);\n  void doDelete(Tokenizer::iterator& beg, Tokenizer::iterator end);\n\n  MemcacheServer* owner_;\n  muduo::net::TcpConnectionPtr conn_;\n  State state_;\n  Protocol protocol_;\n\n  // current request\n  string command_;\n  bool noreply_;\n  Item::UpdatePolicy policy_;\n  ItemPtr currItem_;\n  size_t bytesToDiscard_;\n  // cached\n  ItemPtr needle_;\n  muduo::net::Buffer outputBuf_;\n\n  // per session stats\n  size_t bytesRead_;\n  size_t requestsProcessed_;\n\n  static string kLongestKey;\n};\n\ntypedef std::shared_ptr<Session> SessionPtr;\n\n#endif  // MUDUO_EXAMPLES_MEMCACHED_SERVER_SESSION_H\n"
  },
  {
    "path": "examples/memcached/server/footprint_test.cc",
    "content": "#include \"examples/memcached/server/MemcacheServer.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/inspect/ProcessInspector.h\"\n\n#include <stdio.h>\n#ifdef HAVE_TCMALLOC\n#include <gperftools/heap-profiler.h>\n#include <gperftools/malloc_extension.h>\n#endif\n\nusing namespace muduo::net;\n\nint main(int argc, char* argv[])\n{\n#ifdef HAVE_TCMALLOC\n  MallocExtension::Initialize();\n#endif\n  int items = argc > 1 ? atoi(argv[1]) : 10000;\n  int keylen = argc > 2 ? atoi(argv[2]) : 10;\n  int valuelen = argc > 3 ? atoi(argv[3]) : 100;\n  EventLoop loop;\n  MemcacheServer::Options options;\n  MemcacheServer server(&loop, options);\n\n  printf(\"sizeof(Item) = %zd\\npid = %d\\nitems = %d\\nkeylen = %d\\nvaluelen = %d\\n\",\n         sizeof(Item), getpid(), items, keylen, valuelen);\n  char key[256] = { 0 };\n  string value;\n  for (int i = 0; i < items; ++i)\n  {\n    snprintf(key, sizeof key, \"%0*d\", keylen, i);\n    value.assign(valuelen, \"0123456789\"[i % 10]);\n    ItemPtr item(Item::makeItem(key, 0, 0, valuelen+2, 1));\n    item->append(value.data(), value.size());\n    item->append(\"\\r\\n\", 2);\n    assert(item->endsWithCRLF());\n    bool exists = false;\n    bool stored = server.storeItem(item, Item::kAdd, &exists);\n    assert(stored); (void) stored;\n    assert(!exists);\n  }\n  Inspector::ArgList arg;\n  printf(\"==========\\n%s\\n\",\n         ProcessInspector::overview(HttpRequest::kGet, arg).c_str());\n  // TODO: print bytes per item, overhead percent\n  fflush(stdout);\n#ifdef HAVE_TCMALLOC\n  char buf[8192];\n  MallocExtension::instance()->GetStats(buf, sizeof buf);\n  printf(\"%s\\n\", buf);\n  HeapProfilerDump(\"end\");\n\n/*\n  // only works for tcmalloc_debug\n  int blocks = 0;\n  size_t total = 0;\n  int histogram[kMallocHistogramSize] = { 0, };\n  MallocExtension::instance()->MallocMemoryStats(&blocks, &total, histogram);\n  printf(\"==========\\nblocks = %d\\ntotal = %zd\\n\", blocks, total);\n  for (int i = 0; i < kMallocHistogramSize; ++i)\n  {\n    printf(\"%d = %d\\n\", i, histogram[i]);\n  }\n*/\n#endif\n}\n"
  },
  {
    "path": "examples/memcached/server/server.cc",
    "content": "#include \"examples/memcached/server/MemcacheServer.h\"\n\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/inspect/Inspector.h\"\n\n#include <boost/program_options.hpp>\n\nnamespace po = boost::program_options;\nusing namespace muduo::net;\n\nbool parseCommandLine(int argc, char* argv[], MemcacheServer::Options* options)\n{\n  options->tcpport = 11211;\n  options->gperfport = 11212;\n  options->threads = 4;\n\n  po::options_description desc(\"Allowed options\");\n  desc.add_options()\n      (\"help,h\", \"Help\")\n      (\"port,p\", po::value<uint16_t>(&options->tcpport), \"TCP port\")\n      (\"udpport,U\", po::value<uint16_t>(&options->udpport), \"UDP port\")\n      (\"gperf,g\", po::value<uint16_t>(&options->gperfport), \"port for gperftools\")\n      (\"threads,t\", po::value<int>(&options->threads), \"Number of worker threads\")\n      ;\n\n  po::variables_map vm;\n  po::store(po::parse_command_line(argc, argv, desc), vm);\n  po::notify(vm);\n\n  if (vm.count(\"help\"))\n  {\n    //printf(\"memcached 1.1.0\\n\");\n    return false;\n  }\n  return true;\n}\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  EventLoopThread inspectThread;\n  MemcacheServer::Options options;\n  if (parseCommandLine(argc, argv, &options))\n  {\n    // FIXME: how to destruct it safely ?\n    new Inspector(inspectThread.startLoop(), InetAddress(options.gperfport), \"memcached-debug\");\n\n    MemcacheServer server(&loop, options);\n    server.setThreadNum(options.threads);\n    server.start();\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/multiplexer/CMakeLists.txt",
    "content": "add_executable(multiplex_server multiplexer.cc)\ntarget_link_libraries(multiplex_server muduo_net)\n\nadd_executable(multiplex_server_simple multiplexer_simple.cc)\ntarget_link_libraries(multiplex_server_simple muduo_net)\n\nadd_executable(multiplex_demux demux.cc)\ntarget_link_libraries(multiplex_demux muduo_net)\n\n"
  },
  {
    "path": "examples/multiplexer/demux.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <queue>\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::shared_ptr<TcpClient> TcpClientPtr;\n\n// const int kMaxConns = 1;\nconst size_t kMaxPacketLen = 255;\nconst size_t kHeaderLen = 3;\n\nconst uint16_t kListenPort = 9999;\nconst char* socksIp = \"127.0.0.1\";\nconst uint16_t kSocksPort = 7777;\n\nstruct Entry\n{\n  int connId;\n  TcpClientPtr client;\n  TcpConnectionPtr connection;\n  Buffer pending;\n};\n\nclass DemuxServer : noncopyable\n{\n public:\n  DemuxServer(EventLoop* loop, const InetAddress& listenAddr, const InetAddress& socksAddr)\n    : loop_(loop),\n      server_(loop, listenAddr, \"DemuxServer\"),\n      socksAddr_(socksAddr)\n  {\n    server_.setConnectionCallback(\n        std::bind(&DemuxServer::onServerConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&DemuxServer::onServerMessage, this, _1, _2, _3));\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n  void onServerConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      if (serverConn_)\n      {\n        conn->shutdown();\n      }\n      else\n      {\n        serverConn_ = conn;\n        LOG_INFO << \"onServerConnection set serverConn_\";\n      }\n    }\n    else\n    {\n      if (serverConn_ == conn)\n      {\n        serverConn_.reset();\n        socksConns_.clear();\n\n        LOG_INFO << \"onServerConnection reset serverConn_\";\n      }\n    }\n  }\n\n  void onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    while (buf->readableBytes() > kHeaderLen)\n    {\n      int len = static_cast<uint8_t>(*buf->peek());\n      if (buf->readableBytes() < len + kHeaderLen)\n      {\n        break;\n      }\n      else\n      {\n        int connId = static_cast<uint8_t>(buf->peek()[1]);\n        connId |= (static_cast<uint8_t>(buf->peek()[2]) << 8);\n\n        if (connId != 0)\n        {\n          assert(socksConns_.find(connId) != socksConns_.end());\n          TcpConnectionPtr& socksConn = socksConns_[connId].connection;\n          if (socksConn)\n          {\n            assert(socksConns_[connId].pending.readableBytes() == 0);\n            socksConn->send(buf->peek() + kHeaderLen, len);\n          }\n          else\n          {\n            socksConns_[connId].pending.append(buf->peek() + kHeaderLen, len);\n          }\n        }\n        else\n        {\n          string cmd(buf->peek() + kHeaderLen, len);\n          doCommand(cmd);\n        }\n        buf->retrieve(len + kHeaderLen);\n      }\n    }\n  }\n\n  void doCommand(const string& cmd)\n  {\n    static const string kConn = \"CONN \";\n\n    int connId = atoi(&cmd[kConn.size()]);\n    bool isUp = cmd.find(\" IS UP\") != string::npos;\n    LOG_INFO << \"doCommand \" << connId << \" \" << isUp;\n    if (isUp)\n    {\n      assert(socksConns_.find(connId) == socksConns_.end());\n      char connName[256];\n      snprintf(connName, sizeof connName, \"SocksClient %d\", connId);\n      Entry entry;\n      entry.connId = connId;\n      entry.client.reset(new TcpClient(loop_, socksAddr_, connName));\n      entry.client->setConnectionCallback(\n          std::bind(&DemuxServer::onSocksConnection, this, connId, _1));\n      entry.client->setMessageCallback(\n          std::bind(&DemuxServer::onSocksMessage, this, connId, _1, _2, _3));\n      // FIXME: setWriteCompleteCallback\n      socksConns_[connId] = entry;\n      entry.client->connect();\n    }\n    else\n    {\n      assert(socksConns_.find(connId) != socksConns_.end());\n      TcpConnectionPtr& socksConn = socksConns_[connId].connection;\n      if (socksConn)\n      {\n        socksConn->shutdown();\n      }\n      else\n      {\n        socksConns_.erase(connId);\n      }\n    }\n  }\n\n  void onSocksConnection(int connId, const TcpConnectionPtr& conn)\n  {\n    assert(socksConns_.find(connId) != socksConns_.end());\n    if (conn->connected())\n    {\n      socksConns_[connId].connection = conn;\n      Buffer& pendingData = socksConns_[connId].pending;\n      if (pendingData.readableBytes() > 0)\n      {\n        conn->send(&pendingData);\n      }\n    }\n    else\n    {\n      if (serverConn_)\n      {\n        char buf[256];\n        int len = snprintf(buf, sizeof(buf), \"DISCONNECT %d\\r\\n\", connId);\n        Buffer buffer;\n        buffer.append(buf, len);\n        sendServerPacket(0, &buffer);\n      }\n      else\n      {\n        socksConns_.erase(connId);\n      }\n    }\n  }\n\n  void onSocksMessage(int connId, const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    assert(socksConns_.find(connId) != socksConns_.end());\n    while (buf->readableBytes() > kMaxPacketLen)\n    {\n      Buffer packet;\n      packet.append(buf->peek(), kMaxPacketLen);\n      buf->retrieve(kMaxPacketLen);\n      sendServerPacket(connId, &packet);\n    }\n    if (buf->readableBytes() > 0)\n    {\n      sendServerPacket(connId, buf);\n    }\n  }\n\n  void sendServerPacket(int connId, Buffer* buf)\n  {\n    size_t len = buf->readableBytes();\n    LOG_DEBUG << len;\n    assert(len <= kMaxPacketLen);\n    uint8_t header[kHeaderLen] = {\n      static_cast<uint8_t>(len),\n      static_cast<uint8_t>(connId & 0xFF),\n      static_cast<uint8_t>((connId & 0xFF00) >> 8)\n    };\n    buf->prepend(header, kHeaderLen);\n    if (serverConn_)\n    {\n      serverConn_->send(buf);\n    }\n  }\n\n  EventLoop* loop_;\n  TcpServer server_;\n  TcpConnectionPtr serverConn_;\n  const InetAddress socksAddr_;\n  std::map<int, Entry> socksConns_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(kListenPort);\n  if (argc > 1)\n  {\n    socksIp = argv[1];\n  }\n  InetAddress socksAddr(socksIp, kSocksPort);\n  DemuxServer server(&loop, listenAddr, socksAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/multiplexer/harness/run.sh",
    "content": "#!/bin/sh\n\nCLASSPATH=lib/netty-3.2.4.Final.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-simple-1.6.1.jar:./bin\n\nexport CLASSPATH\nmkdir -p bin\njavac -d bin ./src/com/chenshuo/muduo/example/multiplexer/*.java ./src/com/chenshuo/muduo/example/multiplexer/testcase/*.java\njava -ea -Djava.net.preferIPv4Stack=true com.chenshuo.muduo.example.multiplexer.MultiplexerTest localhost\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/DataEvent.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport java.nio.charset.Charset;\n\nimport org.jboss.netty.buffer.ChannelBuffer;\n\npublic class DataEvent extends Event {\n\n    public final EventSource source;\n    public final int whichClient;\n    public final ChannelBuffer data;\n\n    public DataEvent(EventSource source, int whichClient, ChannelBuffer data) {\n        this.source = source;\n        this.whichClient = whichClient;\n        this.data = data;\n    }\n\n    public String getString() {\n        return data.toString(Charset.defaultCharset());\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/Event.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\npublic class Event {\n\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/EventQueue.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport java.util.concurrent.BlockingDeque;\nimport java.util.concurrent.LinkedBlockingDeque;\nimport java.util.concurrent.TimeUnit;\n\npublic class EventQueue {\n    private BlockingDeque<Event> queue = new LinkedBlockingDeque<Event>();\n    \n    public void put(Event e) {\n        queue.add(e);\n    }\n    \n    public Event take() {\n        try {\n            return queue.poll(5, TimeUnit.SECONDS);\n        } catch (InterruptedException e) {\n            return null;\n        }\n    }\n    \n    public boolean isEmpty() {\n        return queue.isEmpty();\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/EventSource.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\npublic enum EventSource {\n    kBackend, kClient\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MockBackendServer.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer;\n\nimport java.net.InetSocketAddress;\nimport java.nio.charset.Charset;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executor;\n\nimport org.jboss.netty.bootstrap.ServerBootstrap;\nimport org.jboss.netty.buffer.ChannelBuffer;\nimport org.jboss.netty.channel.Channel;\nimport org.jboss.netty.channel.ChannelFactory;\nimport org.jboss.netty.channel.ChannelHandlerContext;\nimport org.jboss.netty.channel.ChannelPipeline;\nimport org.jboss.netty.channel.ChannelPipelineFactory;\nimport org.jboss.netty.channel.ChannelStateEvent;\nimport org.jboss.netty.channel.Channels;\nimport org.jboss.netty.channel.ExceptionEvent;\nimport org.jboss.netty.channel.MessageEvent;\nimport org.jboss.netty.channel.SimpleChannelHandler;\nimport org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;\nimport org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class MockBackendServer {\n    private static final Logger logger = LoggerFactory.getLogger(\"MockBackendServer\");\n\n    private class Handler extends SimpleChannelHandler {\n\n        @Override\n        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n                throws Exception {\n            logger.debug(\"channelConnected {},, {}\", ctx, e);\n            assert connection == null;\n            connection = e.getChannel();\n            latch.countDown();\n        }\n\n        @Override\n        public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n                throws Exception {\n            logger.debug(\"channelDisconnected {},, {}\", ctx, e);\n            assert connection == e.getChannel();\n            connection = null;\n        }\n\n        @Override\n        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)\n                throws Exception {\n            logger.debug(\"messageReceived {},, {}\", ctx, e);\n            assert connection == e.getChannel();\n            ChannelBuffer input = (ChannelBuffer) e.getMessage();\n            int len = input.readUnsignedByte();\n            int whichClient = input.readUnsignedShort();\n            assert len == input.readableBytes();\n            logger.debug(\"From {}, '{}'\", whichClient, input.toString(Charset.defaultCharset()));\n            queue.put(new DataEvent(EventSource.kBackend, whichClient, input));\n        }\n\n        @Override\n        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)\n                throws Exception {\n            logger.error(\"exceptionCaught {},, {}\", ctx, e);\n        }\n    }\n\n    private final EventQueue queue;\n    private final int port;\n    private final Executor boss;\n    private final Executor worker;\n    private final CountDownLatch latch;\n    private Channel listener;\n    private volatile Channel connection;\n\n    public MockBackendServer(EventQueue queue, int listeningPort, Executor boss, Executor worker,\n            CountDownLatch latch) {\n        this.queue = queue;\n        port = listeningPort;\n        this.boss = boss;\n        this.worker = worker;\n        this.latch = latch;\n    }\n\n    public void start() {\n        ServerBootstrap bootstrap = getBootstrap();\n        listener = bootstrap.bind(new InetSocketAddress(port));\n        logger.debug(\"started\");\n    }\n\n    public void sendToClient(int whichClient, ChannelBuffer data) {\n        ChannelBuffer output = data.factory().getBuffer(3);\n        output.writeByte(data.readableBytes());\n        output.writeShort(whichClient);\n        connection.write(wrappedBuffer(output, data));\n    }\n\n    public ChannelBuffer sendToClient(int whichClient, String str) {\n        byte[] bytes = str.getBytes();\n        ChannelBuffer data = MultiplexerTest.bufferFactory.getBuffer(bytes, 0, bytes.length);\n        sendToClient(whichClient, data);\n        return data;\n    }\n\n    public void stop() {\n        listener.close();\n    }\n\n    private ServerBootstrap getBootstrap() {\n        ChannelFactory factory = new NioServerSocketChannelFactory(boss, worker);\n        ServerBootstrap bootstrap = new ServerBootstrap(factory);\n        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {\n            @Override\n            public ChannelPipeline getPipeline() throws Exception {\n                return Channels.pipeline(\n                        new LengthFieldBasedFrameDecoder(255 + 3, 0, 1, 2, 0),\n                        new Handler());\n            }\n        });\n        bootstrap.setOption(\"reuseAddress\", true);\n        bootstrap.setOption(\"child.tcpNoDelay\", true);\n        bootstrap.setOption(\"child.bufferFactory\", MultiplexerTest.bufferFactory);\n        return bootstrap;\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MockClient.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport java.net.InetSocketAddress;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.jboss.netty.bootstrap.ClientBootstrap;\nimport org.jboss.netty.buffer.ChannelBuffer;\nimport org.jboss.netty.channel.Channel;\nimport org.jboss.netty.channel.ChannelFactory;\nimport org.jboss.netty.channel.ChannelFuture;\nimport org.jboss.netty.channel.ChannelHandlerContext;\nimport org.jboss.netty.channel.ChannelPipeline;\nimport org.jboss.netty.channel.ChannelPipelineFactory;\nimport org.jboss.netty.channel.ChannelStateEvent;\nimport org.jboss.netty.channel.Channels;\nimport org.jboss.netty.channel.ExceptionEvent;\nimport org.jboss.netty.channel.MessageEvent;\nimport org.jboss.netty.channel.SimpleChannelHandler;\nimport org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;\nimport org.jboss.netty.util.HashedWheelTimer;\nimport org.jboss.netty.util.Timeout;\nimport org.jboss.netty.util.Timer;\nimport org.jboss.netty.util.TimerTask;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class MockClient {\n    private static final Logger logger = LoggerFactory.getLogger(\"MockClient\");\n\n    private class Handler extends SimpleChannelHandler {\n\n        @Override\n        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n                throws Exception {\n            logger.debug(\"channelConnected {},, {}\", ctx, e);\n            assert connection == null;\n            connection = e.getChannel();\n            if (latch != null)\n                latch.countDown();\n        }\n\n        @Override\n        public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e)\n                throws Exception {\n            logger.debug(\"channelDisconnected {},, {}\", ctx, e);\n            assert connection == e.getChannel();\n            connection = null;\n        }\n\n        @Override\n        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)\n                throws Exception {\n            logger.debug(\"messageReceived {},, {}\", ctx, e);\n            assert connection == e.getChannel();\n            queue.put(new DataEvent(EventSource.kClient, connId, (ChannelBuffer) e.getMessage()));\n        }\n\n        @Override\n        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)\n                throws Exception {\n            logger.error(\"exceptionCaught {},, {}\", ctx, e);\n            // reconnect();\n        }\n\n        @SuppressWarnings(\"unused\")\n        private void reconnect() {\n            timer.newTimeout(new TimerTask() {\n                @Override\n                public void run(Timeout timeout) throws Exception {\n                    logger.info(\"Reconnecting\");\n                    bootstrap.connect();\n\n                }\n            }, 5, TimeUnit.SECONDS);\n        }\n    }\n\n    private final EventQueue queue;\n    private final InetSocketAddress remoteAddress;\n    private final Executor boss;\n    private final Executor worker;\n    private final Timer timer;\n    private volatile Channel connection;\n    private ClientBootstrap bootstrap;\n    private int connId;\n    private MyCountDownLatch latch;\n\n    public MockClient(EventQueue queue, InetSocketAddress remoteAddress, Executor boss,\n            Executor worker) {\n        this.queue = queue;\n        this.remoteAddress = remoteAddress;\n        this.boss = boss;\n        this.worker = worker;\n        this.timer = new HashedWheelTimer();\n        connId = -1;\n    }\n\n    public ChannelFuture connect() {\n        assert bootstrap == null;\n\n        ChannelFactory factory = new NioClientSocketChannelFactory(boss, worker);\n        bootstrap = new ClientBootstrap(factory);\n\n        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {\n            public ChannelPipeline getPipeline() {\n                return Channels.pipeline(new Handler());\n            }\n        });\n\n        bootstrap.setOption(\"tcpNoDelay\", true);\n        bootstrap.setOption(\"remoteAddress\", remoteAddress);\n\n        return bootstrap.connect();\n    }\n\n    public void connectAndWait() {\n        latch = new MyCountDownLatch(1);\n        connect();\n        latch.awaitUninterruptibly(500);\n        assert connection != null;\n    }\n\n    public void send(ChannelBuffer buf) {\n        connection.write(buf);\n    }\n    \n    public ChannelBuffer send(String str) {\n        byte[] bytes = str.getBytes();\n        ChannelBuffer buf = MultiplexerTest.bufferFactory.getBuffer(bytes, 0, bytes.length);\n        connection.write(buf);\n        return buf;\n    }\n    \n    public void disconnect() {\n        connection.close();\n    }\n\n    public void setId(int connId) {\n        assert this.connId == -1;\n        this.connId = connId;\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MultiplexerTest.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport java.net.InetSocketAddress;\nimport java.nio.ByteOrder;\nimport java.util.ArrayList;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.regex.Pattern;\n\nimport org.jboss.netty.buffer.ChannelBufferFactory;\nimport org.jboss.netty.buffer.HeapChannelBufferFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientBothSend;\nimport com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientNoData;\nimport com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientSend;\nimport com.chenshuo.muduo.example.multiplexer.testcase.TestOneClientBackendSend;\nimport com.chenshuo.muduo.example.multiplexer.testcase.TestTwoClients;\n\npublic class MultiplexerTest {\n    private static final Logger logger = LoggerFactory.getLogger(\"MultiplexerTest\");\n    public static final ChannelBufferFactory bufferFactory =\n            HeapChannelBufferFactory.getInstance(ByteOrder.LITTLE_ENDIAN);\n    \n    public final Pattern commandChannel = Pattern.compile(\"CONN (\\\\d+) FROM [0-9.:]+ IS ([A-Z]+)\\r\\n\");\n    \n    private static final int kMultiplexerServerPort = 3333;\n    private static final int kLogicalServerPort = 9999;\n    private final InetSocketAddress multiplexerAddress;\n    private final ExecutorService boss;\n    private final ExecutorService worker;\n    private EventQueue queue;\n    private MyCountDownLatch latch;\n    private MockBackendServer backend;\n    private ArrayList<TestCase> testCases;\n\n    public MultiplexerTest(String multiplexerHost) {\n        multiplexerAddress = new InetSocketAddress(multiplexerHost, kMultiplexerServerPort);\n        boss = Executors.newCachedThreadPool();\n        worker = Executors.newCachedThreadPool();\n        queue = new EventQueue();\n        latch = new MyCountDownLatch(1);\n        backend = new MockBackendServer(queue, kLogicalServerPort, boss, worker, latch);\n        testCases = new ArrayList<TestCase>();\n    }\n\n    public static void main(String[] args) {\n        if (args.length >= 1) {\n            String multiplexerHost = args[0];\n            MultiplexerTest test = new MultiplexerTest(multiplexerHost);\n            test.addTestCase(new TestOneClientNoData());\n            test.addTestCase(new TestOneClientSend());\n            test.addTestCase(new TestOneClientBackendSend());\n            test.addTestCase(new TestOneClientBothSend());\n            test.addTestCase(new TestTwoClients());\n            test.run();\n        } else {\n            System.out.println(\"Usage: ./run.sh path_to_test_data multiplexer_host\");\n            System.out.println(\"Example: ./run.sh localhost\");\n        }\n    }\n\n    private void addTestCase(TestCase testCase) {\n        testCases.add(testCase);\n        testCase.setOwner(this);\n    }\n\n    private void run() {\n        logger.info(\"Waiting for connection\");\n        backend.start();\n        latch.awaitUninterruptibly();\n\n        logger.info(\"Ready\");\n        for (TestCase testCase : testCases) {\n            testCase.test();\n        }\n        System.out.flush();\n        sleep(500);\n        logger.info(\"Finished\");\n        System.exit(0);\n    }\n\n    public MockClient newClient() {\n        MockClient client = new MockClient(queue, multiplexerAddress, boss, worker);\n        client.connectAndWait();\n        return client;\n    }\n\n    public EventQueue getEventQueue() {\n        return queue;\n    }\n    \n    public MockBackendServer getBackend() {\n        return backend;\n    }\n\n    public void sleep(int millis) {\n        try {\n            Thread.sleep(millis);\n        } catch (InterruptedException e) {\n        }\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/MyCountDownLatch.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\n\npublic class MyCountDownLatch extends CountDownLatch {\n\n    public MyCountDownLatch(int count) {\n        super(count);\n    }\n\n    public void awaitUninterruptibly() {\n        try {\n            await();\n        } catch (InterruptedException e) {\n        }\n    }\n\n    public void awaitUninterruptibly(int millis) {\n        try {\n            await(millis, TimeUnit.MILLISECONDS);\n        } catch (InterruptedException e) {\n        }\n    }\n\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/TestCase.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\nimport org.jboss.netty.buffer.ChannelBufferFactory;\n\npublic abstract class TestCase {\n    protected static final ChannelBufferFactory bufferFactory = MultiplexerTest.bufferFactory;\n\n    protected MultiplexerTest god;\n    protected EventQueue queue;\n    protected MockBackendServer backend;\n\n    public void setOwner(MultiplexerTest god) {\n        this.god = god;\n        queue = god.getEventQueue();\n        backend = god.getBackend();\n    }\n\n    public void test() {\n        try {\n            run();\n        } catch (TestFailedException e) {\n            System.out.printf(\"%s FAILED: %s\\n\", this.getClass().getSimpleName(), e.getMessage());\n            e.printStackTrace();\n            return;\n        } catch (Exception e) {\n            System.out.printf(\"%s FATAL: %s\\n\", this.getClass().getSimpleName(), e.toString());\n            e.printStackTrace();\n            return;\n        }\n        System.out.printf(\"%s PASS\\n\", this.getClass().getSimpleName());\n    }\n\n    protected void assertEquals(Object expected, Object actual) {\n        if (!expected.equals(actual))\n            fail(\"assertEquals failed\");\n    }\n\n    protected void assertTrue(boolean yes) {\n        if (!yes)\n            fail(\"assertTrue failed\");\n    }\n\n    protected void fail(String message) {\n        throw new TestFailedException(message);\n    }\n\n    public abstract void run();\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/TestFailedException.java",
    "content": "package com.chenshuo.muduo.example.multiplexer;\n\npublic class TestFailedException extends RuntimeException {\n    private static final long serialVersionUID = 1982L;\n\n    public TestFailedException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientBackendSend.java",
    "content": "package com.chenshuo.muduo.example.multiplexer.testcase;\n\nimport java.nio.charset.Charset;\nimport java.util.regex.Matcher;\n\nimport org.jboss.netty.buffer.ChannelBuffer;\n\nimport com.chenshuo.muduo.example.multiplexer.DataEvent;\nimport com.chenshuo.muduo.example.multiplexer.Event;\nimport com.chenshuo.muduo.example.multiplexer.EventSource;\nimport com.chenshuo.muduo.example.multiplexer.MockClient;\nimport com.chenshuo.muduo.example.multiplexer.TestCase;\n\npublic class TestOneClientBackendSend extends TestCase {\n\n    @Override\n    public void run() {\n        if (!queue.isEmpty())\n            fail(\"EventQueue is not empty\");\n\n        // step 1\n        MockClient client = god.newClient();\n        Event ev = queue.take();\n        DataEvent de = (DataEvent) ev;\n        assertEquals(EventSource.kBackend, de.source);\n\n        Matcher m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        final int connId = Integer.parseInt(m.group(1));\n        assertTrue(connId > 0);\n        client.setId(connId);\n\n        assertEquals(\"UP\", m.group(2));\n\n        // step 2\n        ChannelBuffer buf = backend.sendToClient(connId, \"hello\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kClient, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 3\n        buf = backend.sendToClient(connId, \"World!\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kClient, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < 255; ++i)\n            sb.append('H');\n\n        buf = backend.sendToClient(connId, sb.toString());\n\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kClient, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n\n        // step 4\n        client.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        assertEquals(connId, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientBothSend.java",
    "content": "package com.chenshuo.muduo.example.multiplexer.testcase;\n\nimport java.nio.charset.Charset;\nimport java.util.regex.Matcher;\n\nimport org.jboss.netty.buffer.ChannelBuffer;\n\nimport com.chenshuo.muduo.example.multiplexer.DataEvent;\nimport com.chenshuo.muduo.example.multiplexer.Event;\nimport com.chenshuo.muduo.example.multiplexer.EventSource;\nimport com.chenshuo.muduo.example.multiplexer.MockClient;\nimport com.chenshuo.muduo.example.multiplexer.TestCase;\n\npublic class TestOneClientBothSend extends TestCase {\n\n    @Override\n    public void run() {\n        if (!queue.isEmpty())\n            fail(\"EventQueue is not empty\");\n\n        // step 1\n        MockClient client = god.newClient();\n        Event ev = queue.take();\n        DataEvent de = (DataEvent) ev;\n        assertEquals(EventSource.kBackend, de.source);\n\n        Matcher m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        final int connId = Integer.parseInt(m.group(1));\n        assertTrue(connId > 0);\n        client.setId(connId);\n\n        assertEquals(\"UP\", m.group(2));\n\n        // step 2\n        ChannelBuffer buf = client.send(\"hello\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 3\n        buf = backend.sendToClient(connId, \"World!\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kClient, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 4\n        client.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        assertEquals(connId, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientNoData.java",
    "content": "package com.chenshuo.muduo.example.multiplexer.testcase;\n\nimport java.util.regex.Matcher;\n\nimport com.chenshuo.muduo.example.multiplexer.DataEvent;\nimport com.chenshuo.muduo.example.multiplexer.Event;\nimport com.chenshuo.muduo.example.multiplexer.EventSource;\nimport com.chenshuo.muduo.example.multiplexer.MockClient;\nimport com.chenshuo.muduo.example.multiplexer.TestCase;\n\npublic class TestOneClientNoData extends TestCase {\n\n    @Override\n    public void run() {\n        if (!queue.isEmpty())\n            fail(\"EventQueue is not empty\");\n        \n        MockClient client = god.newClient();\n        Event ev = queue.take();\n        DataEvent de = (DataEvent) ev;\n        assertEquals(EventSource.kBackend, de.source);\n\n        Matcher m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n        \n        int connId = Integer.parseInt(m.group(1));\n        assertTrue(connId > 0);\n        client.setId(connId);\n        \n        assertEquals(\"UP\", m.group(2));\n        \n        client.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n        \n        assertEquals(connId, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestOneClientSend.java",
    "content": "package com.chenshuo.muduo.example.multiplexer.testcase;\n\nimport java.nio.charset.Charset;\nimport java.util.regex.Matcher;\n\nimport org.jboss.netty.buffer.ChannelBuffer;\n\nimport com.chenshuo.muduo.example.multiplexer.DataEvent;\nimport com.chenshuo.muduo.example.multiplexer.Event;\nimport com.chenshuo.muduo.example.multiplexer.EventSource;\nimport com.chenshuo.muduo.example.multiplexer.MockClient;\nimport com.chenshuo.muduo.example.multiplexer.TestCase;\n\npublic class TestOneClientSend extends TestCase {\n\n    @Override\n    public void run() {\n        if (!queue.isEmpty())\n            fail(\"EventQueue is not empty\");\n\n        // step 1\n        MockClient client = god.newClient();\n        Event ev = queue.take();\n        DataEvent de = (DataEvent) ev;\n        assertEquals(EventSource.kBackend, de.source);\n\n        Matcher m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        final int connId = Integer.parseInt(m.group(1));\n        assertTrue(connId > 0);\n        client.setId(connId);\n\n        assertEquals(\"UP\", m.group(2));\n\n        // step 2\n        ChannelBuffer buf = client.send(\"hello\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 3\n        buf = client.send(\"World!\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < 500; ++i)\n            sb.append('H');\n\n        buf = client.send(sb.toString());\n\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf.copy(0, 255), de.data);\n\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId, de.whichClient);\n        assertEquals(buf.copy(255, 245), de.data);\n\n        // step 4\n        client.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        assertEquals(connId, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/harness/src/com/chenshuo/muduo/example/multiplexer/testcase/TestTwoClients.java",
    "content": "package com.chenshuo.muduo.example.multiplexer.testcase;\n\nimport java.nio.charset.Charset;\nimport java.util.regex.Matcher;\n\nimport org.jboss.netty.buffer.ChannelBuffer;\n\nimport com.chenshuo.muduo.example.multiplexer.DataEvent;\nimport com.chenshuo.muduo.example.multiplexer.Event;\nimport com.chenshuo.muduo.example.multiplexer.EventSource;\nimport com.chenshuo.muduo.example.multiplexer.MockClient;\nimport com.chenshuo.muduo.example.multiplexer.TestCase;\n\npublic class TestTwoClients extends TestCase {\n\n    @Override\n    public void run() {\n        if (!queue.isEmpty())\n            fail(\"EventQueue is not empty\");\n\n        // step 1\n        final MockClient client1 = god.newClient();\n        Event ev = queue.take();\n        DataEvent de = (DataEvent) ev;\n        assertEquals(EventSource.kBackend, de.source);\n\n        Matcher m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        final int connId1 = Integer.parseInt(m.group(1));\n        assertTrue(connId1 > 0);\n        client1.setId(connId1);\n        assertEquals(\"UP\", m.group(2));\n\n        // step 2\n        final MockClient client2 = god.newClient();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n\n        final int connId2 = Integer.parseInt(m.group(1));\n        assertTrue(connId2 > 0);\n        client2.setId(connId2);\n        assertEquals(\"UP\", m.group(2));\n\n        ChannelBuffer buf = client1.send(\"hello\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        assertEquals(connId1, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 3\n        buf = backend.sendToClient(connId2, \"World!\");\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kClient, de.source);\n        assertEquals(connId2, de.whichClient);\n        assertEquals(buf, de.data);\n        System.out.println(de.data.toString(Charset.defaultCharset()));\n\n        // step 4\n        client1.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n        assertEquals(connId1, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n\n        client2.disconnect();\n        de = (DataEvent) queue.take();\n        assertEquals(EventSource.kBackend, de.source);\n        m = god.commandChannel.matcher(de.getString());\n        if (!m.matches())\n            fail(\"command channel message doesn't match.\");\n        assertEquals(connId2, Integer.parseInt(m.group(1)));\n        assertEquals(\"DOWN\", m.group(2));\n\n    }\n}\n"
  },
  {
    "path": "examples/multiplexer/multiplexer.cc",
    "content": "#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <queue>\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst int kMaxConns = 10;  // 65535\nconst size_t kMaxPacketLen = 255;\nconst size_t kHeaderLen = 3;\n\nconst uint16_t kClientPort = 3333;\nconst char* backendIp = \"127.0.0.1\";\nconst uint16_t kBackendPort = 9999;\n\nclass MultiplexServer\n{\n public:\n  MultiplexServer(EventLoop* loop,\n                  const InetAddress& listenAddr,\n                  const InetAddress& backendAddr,\n                  int numThreads)\n    : server_(loop, listenAddr, \"MultiplexServer\"),\n      backend_(loop, backendAddr, \"MultiplexBackend\"),\n      numThreads_(numThreads),\n      oldCounter_(0),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&MultiplexServer::onClientConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&MultiplexServer::onClientMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n\n    backend_.setConnectionCallback(\n        std::bind(&MultiplexServer::onBackendConnection, this, _1));\n    backend_.setMessageCallback(\n        std::bind(&MultiplexServer::onBackendMessage, this, _1, _2, _3));\n    backend_.enableRetry();\n\n    // loop->runEvery(10.0, std::bind(&MultiplexServer::printStatistics, this));\n\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads_ << \" threads.\";\n    backend_.connect();\n    server_.start();\n  }\n\n private:\n  void sendBackendPacket(int id, Buffer* buf)\n  {\n    size_t len = buf->readableBytes();\n    assert(len <= kMaxPacketLen);\n    uint8_t header[kHeaderLen] = {\n      static_cast<uint8_t>(len),\n      static_cast<uint8_t>(id & 0xFF),\n      static_cast<uint8_t>((id & 0xFF00) >> 8)\n    };\n    buf->prepend(header, kHeaderLen);\n    TcpConnectionPtr backendConn;\n    {\n      MutexLockGuard lock(mutex_);\n      backendConn = backendConn_;\n    }\n    if (backendConn)\n    {\n      backendConn->send(buf);\n    }\n  }\n\n  void sendBackendString(int id, const string& msg)\n  {\n    assert(msg.size() <= kMaxPacketLen);\n    Buffer buf;\n    buf.append(msg);\n    sendBackendPacket(id, &buf);\n  }\n\n  void sendBackendBuffer(int id, Buffer* buf)\n  {\n    while (buf->readableBytes() > kMaxPacketLen)\n    {\n      Buffer packet;\n      packet.append(buf->peek(), kMaxPacketLen);\n      buf->retrieve(kMaxPacketLen);\n      sendBackendPacket(id, &packet);\n    }\n    if (buf->readableBytes() > 0)\n    {\n      sendBackendPacket(id, buf);\n    }\n  }\n\n  void sendToClient(Buffer* buf)\n  {\n    while (buf->readableBytes() > kHeaderLen)\n    {\n      int len = static_cast<uint8_t>(*buf->peek());\n      if (buf->readableBytes() < len + kHeaderLen)\n      {\n        break;\n      }\n      else\n      {\n        int id = static_cast<uint8_t>(buf->peek()[1]);\n        id |= (static_cast<uint8_t>(buf->peek()[2]) << 8);\n\n        TcpConnectionPtr clientConn;\n        {\n          MutexLockGuard lock(mutex_);\n          std::map<int, TcpConnectionPtr>::iterator it = clientConns_.find(id);\n          if (it != clientConns_.end())\n          {\n            clientConn = it->second;\n          }\n        }\n        if (clientConn)\n        {\n          clientConn->send(buf->peek() + kHeaderLen, len);\n        }\n        buf->retrieve(len + kHeaderLen);\n      }\n    }\n  }\n\n  void onClientConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << \"Client \" << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      int id = -1;\n      {\n        MutexLockGuard lock(mutex_);\n        if (!availIds_.empty())\n        {\n          id = availIds_.front();\n          availIds_.pop();\n          clientConns_[id] = conn;\n        }\n      }\n\n      if (id <= 0)\n      {\n        conn->shutdown();\n      }\n      else\n      {\n        conn->setContext(id);\n        char buf[256];\n        snprintf(buf, sizeof(buf), \"CONN %d FROM %s IS UP\\r\\n\", id,\n                 conn->peerAddress().toIpPort().c_str());\n        sendBackendString(0, buf);\n      }\n    }\n    else\n    {\n      if (!conn->getContext().empty())\n      {\n        int id = boost::any_cast<int>(conn->getContext());\n        assert(id > 0 && id <= kMaxConns);\n        char buf[256];\n        snprintf(buf, sizeof(buf), \"CONN %d FROM %s IS DOWN\\r\\n\",\n                 id, conn->peerAddress().toIpPort().c_str());\n        sendBackendString(0, buf);\n\n        MutexLockGuard lock(mutex_);\n        if (backendConn_)\n        {\n          availIds_.push(id);\n          clientConns_.erase(id);\n        }\n        else\n        {\n          assert(availIds_.empty());\n          assert(clientConns_.empty());\n        }\n      }\n    }\n  }\n\n  void onClientMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    size_t len = buf->readableBytes();\n    transferred_.addAndGet(len);\n    receivedMessages_.incrementAndGet();\n    if (!conn->getContext().empty())\n    {\n      int id = boost::any_cast<int>(conn->getContext());\n      sendBackendBuffer(id, buf);\n      // assert(buf->readableBytes() == 0);\n    }\n    else\n    {\n      buf->retrieveAll();\n      // FIXME: error handling\n    }\n  }\n\n  void onBackendConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << \"Backend \" << conn->localAddress().toIpPort() << \" -> \"\n              << conn->peerAddress().toIpPort() << \" is \"\n              << (conn->connected() ? \"UP\" : \"DOWN\");\n    std::vector<TcpConnectionPtr> connsToDestroy;\n    if (conn->connected())\n    {\n      MutexLockGuard lock(mutex_);\n      backendConn_ = conn;\n      assert(availIds_.empty());\n      for (int i = 1; i <= kMaxConns; ++i)\n      {\n        availIds_.push(i);\n      }\n    }\n    else\n    {\n      MutexLockGuard lock(mutex_);\n      backendConn_.reset();\n      connsToDestroy.reserve(clientConns_.size());\n      for (std::map<int, TcpConnectionPtr>::iterator it = clientConns_.begin();\n          it != clientConns_.end();\n          ++it)\n      {\n        connsToDestroy.push_back(it->second);\n      }\n      clientConns_.clear();\n      while (!availIds_.empty())\n      {\n        availIds_.pop();\n      }\n    }\n\n    for (std::vector<TcpConnectionPtr>::iterator it = connsToDestroy.begin();\n        it != connsToDestroy.end();\n        ++it)\n    {\n      (*it)->shutdown();\n    }\n  }\n\n  void onBackendMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    size_t len = buf->readableBytes();\n    transferred_.addAndGet(len);\n    receivedMessages_.incrementAndGet();\n    sendToClient(buf);\n  }\n\n  void printStatistics()\n  {\n    Timestamp endTime = Timestamp::now();\n    int64_t newCounter = transferred_.get();\n    int64_t bytes = newCounter - oldCounter_;\n    int64_t msgs = receivedMessages_.getAndSet(0);\n    double time = timeDifference(endTime, startTime_);\n    printf(\"%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\\n\",\n        static_cast<double>(bytes)/time/1024/1024,\n        static_cast<double>(msgs)/time/1024,\n        static_cast<double>(bytes)/static_cast<double>(msgs));\n\n    oldCounter_ = newCounter;\n    startTime_ = endTime;\n  }\n\n  TcpServer server_;\n  TcpClient backend_;\n  int numThreads_;\n  AtomicInt64 transferred_;\n  AtomicInt64 receivedMessages_;\n  int64_t oldCounter_;\n  Timestamp startTime_;\n  MutexLock mutex_;\n  TcpConnectionPtr backendConn_ GUARDED_BY(mutex_);\n  std::map<int, TcpConnectionPtr> clientConns_ GUARDED_BY(mutex_);\n  std::queue<int> availIds_ GUARDED_BY(mutex_);\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  int numThreads = 4;\n  if (argc > 1)\n  {\n    backendIp = argv[1];\n  }\n  if (argc > 2)\n  {\n    numThreads = atoi(argv[2]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(kClientPort);\n  InetAddress backendAddr(backendIp, kBackendPort);\n  MultiplexServer server(&loop, listenAddr, backendAddr, numThreads);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/multiplexer/multiplexer_simple.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <queue>\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst int kMaxConns = 10;  // 65535\nconst size_t kMaxPacketLen = 255;\nconst size_t kHeaderLen = 3;\n\nconst uint16_t kClientPort = 3333;\nconst char* backendIp = \"127.0.0.1\";\nconst uint16_t kBackendPort = 9999;\n\nclass MultiplexServer : noncopyable\n{\n public:\n  MultiplexServer(EventLoop* loop, const InetAddress& listenAddr, const InetAddress& backendAddr)\n    : server_(loop, listenAddr, \"MultiplexServer\"),\n      backend_(loop, backendAddr, \"MultiplexBackend\")\n  {\n    server_.setConnectionCallback(\n        std::bind(&MultiplexServer::onClientConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&MultiplexServer::onClientMessage, this, _1, _2, _3));\n    backend_.setConnectionCallback(\n        std::bind(&MultiplexServer::onBackendConnection, this, _1));\n    backend_.setMessageCallback(\n        std::bind(&MultiplexServer::onBackendMessage, this, _1, _2, _3));\n    backend_.enableRetry();\n  }\n\n  void start()\n  {\n    backend_.connect();\n    server_.start();\n  }\n\n private:\n\n  void onClientConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << \"Client \" << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      int id = -1;\n      if (!availIds_.empty())\n      {\n        id = availIds_.front();\n        availIds_.pop();\n        clientConns_[id] = conn;\n      }\n\n      if (id <= 0)\n      {\n        // no client id available\n        conn->shutdown();\n      }\n      else\n      {\n        conn->setContext(id);\n        char buf[256];\n        snprintf(buf, sizeof(buf), \"CONN %d FROM %s IS UP\\r\\n\",\n                 id, conn->peerAddress().toIpPort().c_str());\n        sendBackendString(0, buf);\n      }\n    }\n    else\n    {\n      if (!conn->getContext().empty())\n      {\n        int id = boost::any_cast<int>(conn->getContext());\n        assert(id > 0 && id <= kMaxConns);\n        char buf[256];\n        snprintf(buf, sizeof(buf), \"CONN %d FROM %s IS DOWN\\r\\n\",\n                 id, conn->peerAddress().toIpPort().c_str());\n        sendBackendString(0, buf);\n\n        if (backendConn_)\n        {\n          // put client id back for reusing\n          availIds_.push(id);\n          clientConns_.erase(id);\n        }\n        else\n        {\n          assert(availIds_.empty());\n          assert(clientConns_.empty());\n        }\n      }\n    }\n  }\n\n  void sendBackendString(int id, const string& msg)\n  {\n    assert(msg.size() <= kMaxPacketLen);\n    Buffer buf;\n    buf.append(msg);\n    sendBackendPacket(id, &buf);\n  }\n\n  void onClientMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    if (!conn->getContext().empty())\n    {\n      int id = boost::any_cast<int>(conn->getContext());\n      sendBackendBuffer(id, buf);\n    }\n    else\n    {\n      buf->retrieveAll();\n      // FIXME: error handling\n    }\n  }\n\n  void sendBackendBuffer(int id, Buffer* buf)\n  {\n    while (buf->readableBytes() > kMaxPacketLen)\n    {\n      Buffer packet;\n      packet.append(buf->peek(), kMaxPacketLen);\n      buf->retrieve(kMaxPacketLen);\n      sendBackendPacket(id, &packet);\n    }\n    if (buf->readableBytes() > 0)\n    {\n      sendBackendPacket(id, buf);\n    }\n  }\n\n  void sendBackendPacket(int id, Buffer* buf)\n  {\n    size_t len = buf->readableBytes();\n    LOG_DEBUG << \"sendBackendPacket \" << len;\n    assert(len <= kMaxPacketLen);\n    uint8_t header[kHeaderLen] = {\n      static_cast<uint8_t>(len),\n      static_cast<uint8_t>(id & 0xFF),\n      static_cast<uint8_t>((id & 0xFF00) >> 8)\n    };\n    buf->prepend(header, kHeaderLen);\n    if (backendConn_)\n    {\n      backendConn_->send(buf);\n    }\n  }\n\n  void onBackendConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << \"Backend \" << conn->localAddress().toIpPort() << \" -> \"\n              << conn->peerAddress().toIpPort() << \" is \"\n              << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      backendConn_ = conn;\n      assert(availIds_.empty());\n      for (int i = 1; i <= kMaxConns; ++i)\n      {\n        availIds_.push(i);\n      }\n    }\n    else\n    {\n      backendConn_.reset();\n      for (std::map<int, TcpConnectionPtr>::iterator it = clientConns_.begin();\n          it != clientConns_.end();\n          ++it)\n      {\n        it->second->shutdown();\n      }\n      clientConns_.clear();\n      while (!availIds_.empty())\n      {\n        availIds_.pop();\n      }\n    }\n  }\n\n  void onBackendMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    sendToClient(buf);\n  }\n\n  void sendToClient(Buffer* buf)\n  {\n    while (buf->readableBytes() > kHeaderLen)\n    {\n      int len = static_cast<uint8_t>(*buf->peek());\n      if (buf->readableBytes() < len + kHeaderLen)\n      {\n        break;\n      }\n      else\n      {\n        int id = static_cast<uint8_t>(buf->peek()[1]);\n        id |= (static_cast<uint8_t>(buf->peek()[2]) << 8);\n\n        if (id != 0)\n        {\n          std::map<int, TcpConnectionPtr>::iterator it = clientConns_.find(id);\n          if (it != clientConns_.end())\n          {\n            it->second->send(buf->peek() + kHeaderLen, len);\n          }\n        }\n        else\n        {\n          string cmd(buf->peek() + kHeaderLen, len);\n          LOG_INFO << \"Backend cmd \" << cmd;\n          doCommand(cmd);\n        }\n        buf->retrieve(len + kHeaderLen);\n      }\n    }\n  }\n\n  void doCommand(const string& cmd)\n  {\n    static const string kDisconnectCmd = \"DISCONNECT \";\n\n    if (cmd.size() > kDisconnectCmd.size()\n        && std::equal(kDisconnectCmd.begin(), kDisconnectCmd.end(), cmd.begin()))\n    {\n      int connId = atoi(&cmd[kDisconnectCmd.size()]);\n      std::map<int, TcpConnectionPtr>::iterator it = clientConns_.find(connId);\n      if (it != clientConns_.end())\n      {\n        it->second->shutdown();\n      }\n    }\n  }\n\n  TcpServer server_;\n  TcpClient backend_;\n  // MutexLock mutex_;\n  TcpConnectionPtr backendConn_;\n  std::map<int, TcpConnectionPtr> clientConns_;\n  std::queue<int> availIds_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(kClientPort);\n  if (argc > 1)\n  {\n    backendIp = argv[1];\n  }\n  InetAddress backendAddr(backendIp, kBackendPort);\n  MultiplexServer server(&loop, listenAddr, backendAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/netty/discard/CMakeLists.txt",
    "content": "add_executable(netty_discard_client client.cc)\ntarget_link_libraries(netty_discard_client muduo_net)\n\nadd_executable(netty_discard_server server.cc)\ntarget_link_libraries(netty_discard_server muduo_net)\n\n"
  },
  {
    "path": "examples/netty/discard/client.cc",
    "content": "#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass DiscardClient : noncopyable\n{\n public:\n  DiscardClient(EventLoop* loop, const InetAddress& listenAddr, int size)\n    : loop_(loop),\n      client_(loop, listenAddr, \"DiscardClient\"),\n      message_(size, 'H')\n  {\n    client_.setConnectionCallback(\n        std::bind(&DiscardClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&DiscardClient::onMessage, this, _1, _2, _3));\n    client_.setWriteCompleteCallback(\n        std::bind(&DiscardClient::onWriteComplete, this, _1));\n    //client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      conn->setTcpNoDelay(true);\n      conn->send(message_);\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n  {\n    buf->retrieveAll();\n  }\n\n  void onWriteComplete(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << \"write complete \" << message_.size();\n    conn->send(message_);\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  string message_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 2009);\n\n    int size = 256;\n    if (argc > 2)\n    {\n      size = atoi(argv[2]);\n    }\n\n    DiscardClient client(&loop, serverAddr, size);\n    client.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip [msg_size]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/netty/discard/server.cc",
    "content": "#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint numThreads = 0;\n\nclass DiscardServer\n{\n public:\n  DiscardServer(EventLoop* loop, const InetAddress& listenAddr)\n    : server_(loop, listenAddr, \"DiscardServer\"),\n      oldCounter_(0),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&DiscardServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&DiscardServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n    loop->runEvery(3.0, std::bind(&DiscardServer::printThroughput, this));\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads << \" threads.\";\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n              << conn->localAddress().toIpPort() << \" is \"\n              << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    size_t len = buf->readableBytes();\n    transferred_.add(len);\n    receivedMessages_.incrementAndGet();\n    buf->retrieveAll();\n  }\n\n  void printThroughput()\n  {\n    Timestamp endTime = Timestamp::now();\n    int64_t newCounter = transferred_.get();\n    int64_t bytes = newCounter - oldCounter_;\n    int64_t msgs = receivedMessages_.getAndSet(0);\n    double time = timeDifference(endTime, startTime_);\n    printf(\"%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\\n\",\n        static_cast<double>(bytes)/time/1024/1024,\n        static_cast<double>(msgs)/time/1024,\n        static_cast<double>(bytes)/static_cast<double>(msgs));\n\n    oldCounter_ = newCounter;\n    startTime_ = endTime;\n  }\n\n  TcpServer server_;\n\n  AtomicInt64 transferred_;\n  AtomicInt64 receivedMessages_;\n  int64_t oldCounter_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(2009);\n  DiscardServer server(&loop, listenAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/netty/echo/CMakeLists.txt",
    "content": "add_executable(netty_echo_client client.cc)\ntarget_link_libraries(netty_echo_client muduo_net)\n\nadd_executable(netty_echo_server server.cc)\ntarget_link_libraries(netty_echo_server muduo_net)\n\nadd_executable(netty_echo_server2 server2.cc)\ntarget_link_libraries(netty_echo_server2 muduo_net)\n\n"
  },
  {
    "path": "examples/netty/echo/client.cc",
    "content": "#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass EchoClient : noncopyable\n{\n public:\n  EchoClient(EventLoop* loop, const InetAddress& listenAddr, int size)\n    : loop_(loop),\n      client_(loop, listenAddr, \"EchoClient\"),\n      message_(size, 'H')\n  {\n    client_.setConnectionCallback(\n        std::bind(&EchoClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&EchoClient::onMessage, this, _1, _2, _3));\n    //client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      conn->setTcpNoDelay(true);\n      conn->send(message_);\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n  {\n    conn->send(buf);\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  string message_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 2007);\n\n    int size = 256;\n    if (argc > 2)\n    {\n      size = atoi(argv[2]);\n    }\n\n    EchoClient client(&loop, serverAddr, size);\n    client.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip [msg_size]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/netty/echo/server.cc",
    "content": "#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint numThreads = 0;\n\nclass EchoServer\n{\n public:\n  EchoServer(EventLoop* loop, const InetAddress& listenAddr)\n    : server_(loop, listenAddr, \"EchoServer\"),\n      oldCounter_(0),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&EchoServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n    loop->runEvery(3.0, std::bind(&EchoServer::printThroughput, this));\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads << \" threads.\";\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    conn->setTcpNoDelay(true);\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    size_t len = buf->readableBytes();\n    transferred_.addAndGet(len);\n    receivedMessages_.incrementAndGet();\n    conn->send(buf);\n  }\n\n  void printThroughput()\n  {\n    Timestamp endTime = Timestamp::now();\n    int64_t newCounter = transferred_.get();\n    int64_t bytes = newCounter - oldCounter_;\n    int64_t msgs = receivedMessages_.getAndSet(0);\n    double time = timeDifference(endTime, startTime_);\n    printf(\"%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\\n\",\n        static_cast<double>(bytes)/time/1024/1024,\n        static_cast<double>(msgs)/time/1024,\n        static_cast<double>(bytes)/static_cast<double>(msgs));\n\n    oldCounter_ = newCounter;\n    startTime_ = endTime;\n  }\n\n  TcpServer server_;\n  AtomicInt64 transferred_;\n  AtomicInt64 receivedMessages_;\n  int64_t oldCounter_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(2007);\n  EchoServer server(&loop, listenAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/netty/echo/server2.cc",
    "content": "#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint numThreads = 0;\n\nclass EchoServer\n{\n public:\n  EchoServer(EventLoop* loop, const InetAddress& listenAddr)\n    : server_(loop, listenAddr, \"EchoServer\"),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&EchoServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n    loop->runEvery(5.0, std::bind(&EchoServer::printThroughput, this));\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads << \" threads.\";\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    conn->setTcpNoDelay(true);\n    if (conn->connected())\n    {\n      connections_.increment();\n    }\n    else\n    {\n      connections_.decrement();\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    size_t len = buf->readableBytes();\n    transferredBytes_.addAndGet(len);\n    receivedMessages_.incrementAndGet();\n    conn->send(buf);\n  }\n\n  void printThroughput()\n  {\n    Timestamp endTime = Timestamp::now();\n    double bytes = static_cast<double>(transferredBytes_.getAndSet(0));\n    int msgs = receivedMessages_.getAndSet(0);\n    double bytesPerMsg = msgs > 0 ?  bytes/msgs : 0;\n    double time = timeDifference(endTime, startTime_);\n    printf(\"%.3f MiB/s %.2f Kilo Msgs/s %.2f bytes per msg, \",\n        bytes/time/1024/1024,\n        static_cast<double>(msgs)/time/1000,\n        bytesPerMsg);\n\n    printConnection();\n    fflush(stdout);\n    startTime_ = endTime;\n  }\n\n  void printConnection()\n  {\n    string procStatus = ProcessInfo::procStatus();\n    printf(\"%d conn, files %d , VmSize %ld KiB, RSS %ld KiB, \",\n           connections_.get(),\n           ProcessInfo::openedFiles(),\n           getLong(procStatus, \"VmSize:\"),\n           getLong(procStatus, \"VmRSS:\"));\n\n    string meminfo;\n    FileUtil::readFile(\"/proc/meminfo\", 65536, &meminfo);\n    long total_kb = getLong(meminfo, \"MemTotal:\");\n    long free_kb = getLong(meminfo, \"MemFree:\");\n    long buffers_kb = getLong(meminfo, \"Buffers:\");\n    long cached_kb = getLong(meminfo, \"Cached:\");\n    printf(\"system memory used %ld KiB\\n\",\n           total_kb - free_kb - buffers_kb - cached_kb);\n  }\n\n  long getLong(const string& procStatus, const char* key)\n  {\n    long result = 0;\n    size_t pos = procStatus.find(key);\n    if (pos != string::npos)\n    {\n      result = ::atol(procStatus.c_str() + pos + strlen(key));\n    }\n    return result;\n  }\n\n  TcpServer server_;\n  AtomicInt32 connections_;\n  AtomicInt32 receivedMessages_;\n  AtomicInt64 transferredBytes_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid()\n           << \", tid = \" << CurrentThread::tid()\n           << \", max files = \" << ProcessInfo::maxOpenFiles();\n  Logger::setLogLevel(Logger::WARN);\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(2007);\n  EchoServer server(&loop, listenAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/netty/uptime/CMakeLists.txt",
    "content": "add_executable(netty_uptime uptime.cc)\ntarget_link_libraries(netty_uptime muduo_net)\n\n"
  },
  {
    "path": "examples/netty/uptime/uptime.cc",
    "content": "#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass UptimeClient : noncopyable\n{\n public:\n  UptimeClient(EventLoop* loop, const InetAddress& listenAddr)\n    : client_(loop, listenAddr, \"UptimeClient\")\n  {\n    client_.setConnectionCallback(\n        std::bind(&UptimeClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&UptimeClient::onMessage, this, _1, _2, _3));\n    //client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n  {\n  }\n\n  TcpClient client_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 2)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(argv[1], port);\n\n    UptimeClient client(&loop, serverAddr);\n    client.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip port\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/pingpong/CMakeLists.txt",
    "content": "add_executable(pingpong_client client.cc)\ntarget_link_libraries(pingpong_client muduo_net)\n\nadd_executable(pingpong_server server.cc)\ntarget_link_libraries(pingpong_server muduo_net)\n\nadd_executable(pingpong_bench bench.cc)\ntarget_link_libraries(pingpong_bench muduo_net)\n\n"
  },
  {
    "path": "examples/pingpong/bench.cc",
    "content": "// Benchmark inspired by libevent/test/bench.c\n// See also: http://libev.schmorp.de/bench.html\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <stdio.h>\n#include <sys/resource.h>\n#include <sys/socket.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstd::vector<int> g_pipes;\nint numPipes;\nint numActive;\nint numWrites;\nEventLoop* g_loop;\nstd::vector<std::unique_ptr<Channel>> g_channels;\n\nint g_reads, g_writes, g_fired;\n\nvoid readCallback(Timestamp, int fd, int idx)\n{\n  char ch;\n\n  g_reads += static_cast<int>(::recv(fd, &ch, sizeof(ch), 0));\n  if (g_writes > 0)\n  {\n    int widx = idx+1;\n    if (widx >= numPipes)\n    {\n      widx -= numPipes;\n    }\n    ::send(g_pipes[2 * widx + 1], \"m\", 1, 0);\n    g_writes--;\n    g_fired++;\n  }\n  if (g_fired == g_reads)\n  {\n    g_loop->quit();\n  }\n}\n\nstd::pair<int, int> runOnce()\n{\n  Timestamp beforeInit(Timestamp::now());\n  for (int i = 0; i < numPipes; ++i)\n  {\n    Channel& channel = *g_channels[i];\n    channel.setReadCallback(std::bind(readCallback, _1, channel.fd(), i));\n    channel.enableReading();\n  }\n\n  int space = numPipes / numActive;\n  space *= 2;\n  for (int i = 0; i < numActive; ++i)\n  {\n    ::send(g_pipes[i * space + 1], \"m\", 1, 0);\n  }\n\n  g_fired = numActive;\n  g_reads = 0;\n  g_writes = numWrites;\n  Timestamp beforeLoop(Timestamp::now());\n  g_loop->loop();\n\n  Timestamp end(Timestamp::now());\n\n  int iterTime = static_cast<int>(end.microSecondsSinceEpoch() - beforeInit.microSecondsSinceEpoch());\n  int loopTime = static_cast<int>(end.microSecondsSinceEpoch() - beforeLoop.microSecondsSinceEpoch());\n  return std::make_pair(iterTime, loopTime);\n}\n\nint main(int argc, char* argv[])\n{\n  numPipes = 100;\n  numActive = 1;\n  numWrites = 100;\n  int c;\n  while ((c = getopt(argc, argv, \"n:a:w:\")) != -1)\n  {\n    switch (c)\n    {\n      case 'n':\n        numPipes = atoi(optarg);\n        break;\n      case 'a':\n        numActive = atoi(optarg);\n        break;\n      case 'w':\n        numWrites = atoi(optarg);\n        break;\n      default:\n        fprintf(stderr, \"Illegal argument \\\"%c\\\"\\n\", c);\n        return 1;\n    }\n  }\n\n  struct rlimit rl;\n  rl.rlim_cur = rl.rlim_max = numPipes * 2 + 50;\n  if (::setrlimit(RLIMIT_NOFILE, &rl) == -1)\n  {\n    perror(\"setrlimit\");\n    //return 1;  // comment out this line if under valgrind\n  }\n  g_pipes.resize(2 * numPipes);\n  for (int i = 0; i < numPipes; ++i)\n  {\n    if (::socketpair(AF_UNIX, SOCK_STREAM, 0, &g_pipes[i*2]) == -1)\n    {\n      perror(\"pipe\");\n      return 1;\n    }\n  }\n\n  EventLoop loop;\n  g_loop = &loop;\n\n  for (int i = 0; i < numPipes; ++i)\n  {\n    Channel* channel = new Channel(&loop, g_pipes[i*2]);\n    g_channels.emplace_back(channel);\n  }\n\n  for (int i = 0; i < 25; ++i)\n  {\n    std::pair<int, int> t = runOnce();\n    printf(\"%8d %8d\\n\", t.first, t.second);\n  }\n\n  for (const auto& channel : g_channels)\n  {\n    channel->disableAll();\n    channel->remove();\n  }\n  g_channels.clear();\n}\n\n"
  },
  {
    "path": "examples/pingpong/client.cc",
    "content": "#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass Client;\n\nclass Session : noncopyable\n{\n public:\n  Session(EventLoop* loop,\n          const InetAddress& serverAddr,\n          const string& name,\n          Client* owner)\n    : client_(loop, serverAddr, name),\n      owner_(owner),\n      bytesRead_(0),\n      bytesWritten_(0),\n      messagesRead_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&Session::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&Session::onMessage, this, _1, _2, _3));\n  }\n\n  void start()\n  {\n    client_.connect();\n  }\n\n  void stop()\n  {\n    client_.disconnect();\n  }\n\n  int64_t bytesRead() const\n  {\n     return bytesRead_;\n  }\n\n  int64_t messagesRead() const\n  {\n     return messagesRead_;\n  }\n\n private:\n\n  void onConnection(const TcpConnectionPtr& conn);\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    ++messagesRead_;\n    bytesRead_ += buf->readableBytes();\n    bytesWritten_ += buf->readableBytes();\n    conn->send(buf);\n  }\n\n  TcpClient client_;\n  Client* owner_;\n  int64_t bytesRead_;\n  int64_t bytesWritten_;\n  int64_t messagesRead_;\n};\n\nclass Client : noncopyable\n{\n public:\n  Client(EventLoop* loop,\n         const InetAddress& serverAddr,\n         int blockSize,\n         int sessionCount,\n         int timeout,\n         int threadCount)\n    : loop_(loop),\n      threadPool_(loop, \"pingpong-client\"),\n      sessionCount_(sessionCount),\n      timeout_(timeout)\n  {\n    loop->runAfter(timeout, std::bind(&Client::handleTimeout, this));\n    if (threadCount > 1)\n    {\n      threadPool_.setThreadNum(threadCount);\n    }\n    threadPool_.start();\n\n    for (int i = 0; i < blockSize; ++i)\n    {\n      message_.push_back(static_cast<char>(i % 128));\n    }\n\n    for (int i = 0; i < sessionCount; ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"C%05d\", i);\n      Session* session = new Session(threadPool_.getNextLoop(), serverAddr, buf, this);\n      session->start();\n      sessions_.emplace_back(session);\n    }\n  }\n\n  const string& message() const\n  {\n    return message_;\n  }\n\n  void onConnect()\n  {\n    if (numConnected_.incrementAndGet() == sessionCount_)\n    {\n      LOG_WARN << \"all connected\";\n    }\n  }\n\n  void onDisconnect(const TcpConnectionPtr& conn)\n  {\n    if (numConnected_.decrementAndGet() == 0)\n    {\n      LOG_WARN << \"all disconnected\";\n\n      int64_t totalBytesRead = 0;\n      int64_t totalMessagesRead = 0;\n      for (const auto& session : sessions_)\n      {\n        totalBytesRead += session->bytesRead();\n        totalMessagesRead += session->messagesRead();\n      }\n      LOG_WARN << totalBytesRead << \" total bytes read\";\n      LOG_WARN << totalMessagesRead << \" total messages read\";\n      LOG_WARN << static_cast<double>(totalBytesRead) / static_cast<double>(totalMessagesRead)\n               << \" average message size\";\n      LOG_WARN << static_cast<double>(totalBytesRead) / (timeout_ * 1024 * 1024)\n               << \" MiB/s throughput\";\n      conn->getLoop()->queueInLoop(std::bind(&Client::quit, this));\n    }\n  }\n\n private:\n\n  void quit()\n  {\n    loop_->queueInLoop(std::bind(&EventLoop::quit, loop_));\n  }\n\n  void handleTimeout()\n  {\n    LOG_WARN << \"stop\";\n    for (auto& session : sessions_)\n    {\n      session->stop();\n    }\n  }\n\n  EventLoop* loop_;\n  EventLoopThreadPool threadPool_;\n  int sessionCount_;\n  int timeout_;\n  std::vector<std::unique_ptr<Session>> sessions_;\n  string message_;\n  AtomicInt32 numConnected_;\n};\n\nvoid Session::onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n    conn->send(owner_->message());\n    owner_->onConnect();\n  }\n  else\n  {\n    owner_->onDisconnect(conn);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc != 7)\n  {\n    fprintf(stderr, \"Usage: client <host_ip> <port> <threads> <blocksize> \");\n    fprintf(stderr, \"<sessions> <time>\\n\");\n  }\n  else\n  {\n    LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n    Logger::setLogLevel(Logger::WARN);\n\n    const char* ip = argv[1];\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    int threadCount = atoi(argv[3]);\n    int blockSize = atoi(argv[4]);\n    int sessionCount = atoi(argv[5]);\n    int timeout = atoi(argv[6]);\n\n    EventLoop loop;\n    InetAddress serverAddr(ip, port);\n\n    Client client(&loop, serverAddr, blockSize, sessionCount, timeout, threadCount);\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/pingpong/server.cc",
    "content": "#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n  }\n}\n\nvoid onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n{\n  conn->send(buf);\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 4)\n  {\n    fprintf(stderr, \"Usage: server <address> <port> <threads>\\n\");\n  }\n  else\n  {\n    LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n    Logger::setLogLevel(Logger::WARN);\n\n    const char* ip = argv[1];\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress listenAddr(ip, port);\n    int threadCount = atoi(argv[3]);\n\n    EventLoop loop;\n\n    TcpServer server(&loop, listenAddr, \"PingPong\");\n\n    server.setConnectionCallback(onConnection);\n    server.setMessageCallback(onMessage);\n\n    if (threadCount > 1)\n    {\n      server.setThreadNum(threadCount);\n    }\n\n    server.start();\n\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/procmon/CMakeLists.txt",
    "content": "add_executable(procmon procmon.cc plot.cc)\ntarget_link_libraries(procmon muduo_http gd)\n\nadd_executable(plot_test plot_test.cc plot.cc)\ntarget_link_libraries(plot_test muduo_base gd)\n\nadd_executable(dummyload dummyload.cc)\ntarget_link_libraries(dummyload muduo_net)\n"
  },
  {
    "path": "examples/procmon/dummyload.cc",
    "content": "#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <math.h>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint g_cycles = 0;\nint g_percent = 82;\nAtomicInt32 g_done;\nbool g_busy = false;\nMutexLock g_mutex;\nCondition g_cond(g_mutex);\n\ndouble busy(int cycles)\n{\n  double result = 0;\n  for (int i = 0; i < cycles; ++i)\n  {\n    result += sqrt(i) * sqrt(i+1);\n  }\n  return result;\n}\n\ndouble getSeconds(int cycles)\n{\n  Timestamp start = Timestamp::now();\n  busy(cycles);\n  return timeDifference(Timestamp::now(), start);\n}\n\nvoid findCycles()\n{\n  g_cycles = 1000;\n  while (getSeconds(g_cycles) < 0.001)\n    g_cycles = g_cycles + g_cycles / 4;  // * 1.25\n  printf(\"cycles %d\\n\", g_cycles);\n}\n\nvoid threadFunc()\n{\n  while (g_done.get() == 0)\n  {\n    {\n    MutexLockGuard guard(g_mutex);\n    while (!g_busy)\n      g_cond.wait();\n    }\n    busy(g_cycles);\n  }\n  printf(\"thread exit\\n\");\n}\n\n// this is open-loop control\nvoid load(int percent)\n{\n  percent = std::max(0, percent);\n  percent = std::min(100, percent);\n\n  // Bresenham's line algorithm\n  int err = 2*percent - 100;\n  int count = 0;\n\n  for (int i = 0; i < 100; ++i)\n  {\n    bool busy = false;\n    if (err > 0)\n    {\n      busy = true;\n      err += 2*(percent - 100);\n      ++count;\n      // printf(\"%2d, \", i);\n    }\n    else\n    {\n      err += 2*percent;\n    }\n\n    {\n    MutexLockGuard guard(g_mutex);\n    g_busy = busy;\n    g_cond.notifyAll();\n    }\n\n    CurrentThread::sleepUsec(10*1000); // 10 ms\n  }\n  assert(count == percent);\n}\n\nvoid fixed()\n{\n  while (true)\n  {\n    load(g_percent);\n  }\n}\n\nvoid cosine()\n{\n  while (true)\n    for (int i = 0; i < 200; ++i)\n    {\n      int percent = static_cast<int>((1.0 + cos(i * 3.14159 / 100)) / 2 * g_percent + 0.5);\n      load(percent);\n    }\n}\n\nvoid sawtooth()\n{\n  while (true)\n    for (int i = 0; i <= 100; ++i)\n    {\n      int percent = static_cast<int>(i / 100.0 * g_percent);\n      load(percent);\n    }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 2)\n  {\n    printf(\"Usage: %s [fctsz] [percent] [num_threads]\\n\", argv[0]);\n    return 0;\n  }\n\n  printf(\"pid %d\\n\", getpid());\n  findCycles();\n\n  g_percent = argc > 2 ? atoi(argv[2]) : 43;\n  int numThreads = argc > 3 ? atoi(argv[3]) : 1;\n  std::vector<std::unique_ptr<Thread>> threads;\n  for (int i = 0; i < numThreads; ++i)\n  {\n    threads.emplace_back(new Thread(threadFunc));\n    threads.back()->start();\n  }\n\n  switch (argv[1][0])\n  {\n    case 'f':\n    {\n      fixed();\n    }\n    break;\n\n    case 'c':\n    {\n      cosine();\n    }\n    break;\n\n    case 'z':\n    {\n      sawtooth();\n    }\n    break;\n\n    // TODO: square and triangle waves\n\n    default:\n    break;\n  }\n\n  g_done.getAndSet(1);\n  {\n  MutexLockGuard guard(g_mutex);\n  g_busy = true;\n  g_cond.notifyAll();\n  }\n  for (int i = 0; i < numThreads; ++i)\n  {\n    threads[i]->join();\n  }\n}\n"
  },
  {
    "path": "examples/procmon/plot.cc",
    "content": "#include \"examples/procmon/plot.h\"\n#include <algorithm>\n\n#include <math.h>\n\n#include <gd.h>\n#include <gdfonts.h>\n\nstruct Plot::MyGdFont : public gdFont {};\n\nPlot::Plot(int width, int height, int totalSeconds, int samplingPeriod)\n  : width_(width),\n    height_(height),\n    totalSeconds_(totalSeconds),\n    samplingPeriod_(samplingPeriod),\n    image_(gdImageCreate(width_, height_)),\n    font_(static_cast<MyGdFont*>(gdFontGetSmall())),\n    fontWidth_(font_->w),\n    fontHeight_(font_->h),\n    background_(gdImageColorAllocate(image_, 255, 255, 240)),\n    black_(gdImageColorAllocate(image_, 0, 0, 0)),\n    gray_(gdImageColorAllocate(image_, 200, 200, 200)),\n    blue_(gdImageColorAllocate(image_, 128, 128, 255)),\n    kRightMargin_(3 * fontWidth_ + 5),\n    ratioX_(static_cast<double>(samplingPeriod_ * (width_ - kLeftMargin_ - kRightMargin_)) / totalSeconds_)\n{\n  // gdImageSetAntiAliased(image_, black_);\n}\n\nPlot::~Plot()\n{\n  gdImageDestroy(image_);\n}\n\nmuduo::string Plot::plotCpu(const std::vector<double>& data)\n{\n  gdImageFilledRectangle(image_, 0, 0, width_, height_, background_);\n  if (data.size() > 1)\n  {\n    gdImageSetThickness(image_, 2);\n    double max = *std::max_element(data.begin(), data.end());\n    if (max >= 10.0)\n    {\n      max = ceil(max);\n    }\n    else\n    {\n      max = std::max(0.1, ceil(max*10.0) / 10.0);\n    }\n    label(max);\n\n    for (size_t i = 0; i < data.size()-1; ++i)\n    {\n      gdImageLine(image_,\n                  getX(i, data.size()),\n                  getY(data[i] / max),\n                  getX(i+1, data.size()),\n                  getY(data[i+1]/max),\n                  black_);\n    }\n  }\n\n  int total = totalSeconds_/samplingPeriod_;\n  gdImageSetThickness(image_, 1);\n  gdImageLine(image_, getX(0, total), getY(0)+2, getX(total, total), getY(0)+2, gray_);\n  gdImageLine(image_, getX(total, total), getY(0)+2, getX(total, total), getY(1)+2, gray_);\n  return toPng();\n}\n\nvoid Plot::label(double maxValue)\n{\n    char buf[64];\n    if (maxValue >= 10.0)\n      snprintf(buf, sizeof buf, \"%.0f\", maxValue);\n    else\n      snprintf(buf, sizeof buf, \"%.1f\", maxValue);\n\n    gdImageString(image_,\n                  font_,\n                  width_ - kRightMargin_ + 3,\n                  kMarginY_ - 3,\n                  reinterpret_cast<unsigned char*>(buf),\n                  black_);\n\n    snprintf(buf, sizeof buf, \"0\");\n    gdImageString(image_,\n                  font_,\n                  width_ - kRightMargin_ + 3,\n                  height_ - kMarginY_ - 3 - fontHeight_ / 2,\n                  reinterpret_cast<unsigned char*>(buf),\n                  gray_);\n\n    snprintf(buf, sizeof buf, \"-%ds\", totalSeconds_);\n    gdImageString(image_,\n                  font_,\n                  kLeftMargin_,\n                  height_ - kMarginY_ - fontHeight_,\n                  reinterpret_cast<unsigned char*>(buf),\n                  blue_);\n}\n\nint Plot::getX(ssize_t i, ssize_t total) const\n{\n  double x = (width_ - kLeftMargin_ - kRightMargin_) + static_cast<double>(i - total) * ratioX_;\n  return static_cast<int>(x + 0.5) + kLeftMargin_;\n}\n\nint Plot::getY(double value) const\n{\n  return static_cast<int>((1.0 - value) * (height_-2*kMarginY_) + 0.5) + kMarginY_;\n}\n\nmuduo::string Plot::toPng()\n{\n  int size = 0;\n  void* png = gdImagePngPtr(image_, &size);\n  muduo::string result(static_cast<char*>(png), size);\n  gdFree(png);\n  return result;\n}\n"
  },
  {
    "path": "examples/procmon/plot.h",
    "content": "#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/Types.h\"\n#include <vector>\n#include <stdlib.h> // ssize_t\n\ntypedef struct gdImageStruct* gdImagePtr;\n\nclass Plot : muduo::noncopyable\n{\n public:\n  Plot(int width, int height, int totalSeconds, int samplingPeriod);\n  ~Plot();\n  muduo::string plotCpu(const std::vector<double>& data);\n\n private:\n  muduo::string toPng();\n  // pair<shared_ptr<void*>, int> toPng();\n  int getX(ssize_t x, ssize_t total) const;\n  int getY(double value) const;\n  void label(double maxValue);\n\n  // gdFont is a typedef of unnamed struct, cannot be forward declared\n  // wordaround suggested in http://stackoverflow.com/questions/7256436/forward-declarations-of-unnamed-struct\n  struct MyGdFont;\n  typedef struct MyGdFont* MyGdFontPtr;\n\n  const int width_;\n  const int height_;\n  const int totalSeconds_;\n  const int samplingPeriod_;\n  gdImagePtr const image_;\n  MyGdFontPtr const font_;\n  const int fontWidth_;\n  const int fontHeight_;\n  const int background_;\n  const int black_;\n  const int gray_;\n  const int blue_;\n\n  const int kRightMargin_;\n  static const int kLeftMargin_ = 5;\n  static const int kMarginY_ = 5;\n\n  const double ratioX_;\n};\n\n"
  },
  {
    "path": "examples/procmon/plot_test.cc",
    "content": "#include \"examples/procmon/plot.h\"\n#include \"muduo/base/Timestamp.h\"\n#include <vector>\n#include <math.h>\n#include <stdio.h>\n\nint main()\n{\n  std::vector<double> cpu_usage;\n  cpu_usage.reserve(300);\n  for (int i = 0; i < 300; ++i)\n    cpu_usage.push_back(1.0 + sin(pow(i / 30.0, 2)));\n  Plot plot(640, 100, 600, 2);\n  muduo::Timestamp start(muduo::Timestamp::now());\n  const int N = 10000;\n  for (int i = 0; i < N; ++i)\n    muduo::string png = plot.plotCpu(cpu_usage);\n  double elapsed = timeDifference(muduo::Timestamp::now(), start);\n  printf(\"%d plots in %f seconds, %f PNG per second, %f ms per PNG\\n\",\n         N, elapsed, N / elapsed, elapsed * 1000 / N);\n  muduo::string png = plot.plotCpu(cpu_usage);\n\n  FILE* fp = fopen(\"test.png\", \"wb\");\n  fwrite(png.data(), 1, png.size(), fp);\n  fclose(fp);\n  printf(\"Image saved to test.png\\n\");\n}\n"
  },
  {
    "path": "examples/procmon/procmon.cc",
    "content": "#include \"examples/procmon/plot.h\"\n\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpResponse.h\"\n#include \"muduo/net/http/HttpServer.h\"\n\n#include <boost/algorithm/string/replace.hpp>\n#include <boost/circular_buffer.hpp>\n\n#include <sstream>\n#include <type_traits>\n\n#include <dirent.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n// TODO:\n// - what if process exits?\n//\n\n// Represent parsed /proc/pid/stat\nstruct StatData\n{\n  void parse(const char* startAtState, int kbPerPage)\n  {\n    // istringstream is probably not the most efficient way to parse it,\n    // see muduo-protorpc/examples/collect/ProcFs.cc for alternatives.\n    std::istringstream iss(startAtState);\n\n    //            0    1    2    3     4    5       6   7 8 9  11  13   15\n    // 3770 (cat) R 3718 3770 3718 34818 3770 4202496 214 0 0 0 0 0 0 0 20\n    // 16  18     19      20 21                   22      23      24              25\n    //  0 1 0 298215 5750784 81 18446744073709551615 4194304 4242836 140736345340592\n    //              26\n    // 140736066274232 140575670169216 0 0 0 0 0 0 0 17 0 0 0 0 0 0\n\n    iss >> state;\n    iss >> ppid >> pgrp >> session >> tty_nr >> tpgid >> flags;\n    iss >> minflt >> cminflt >> majflt >> cmajflt;\n    iss >> utime >> stime >> cutime >> cstime;\n    iss >> priority >> nice >> num_threads >> itrealvalue >> starttime;\n    long vsize, rss;\n    iss >> vsize >> rss >> rsslim;\n    vsizeKb = vsize / 1024;\n    rssKb = rss * kbPerPage;\n  }\n  // int pid;\n  char state;\n  int ppid;\n  int pgrp;\n  int session;\n  int tty_nr;\n  int tpgid;\n  int flags;\n\n  long minflt;\n  long cminflt;\n  long majflt;\n  long cmajflt;\n\n  long utime;\n  long stime;\n  long cutime;\n  long cstime;\n\n  long priority;\n  long nice;\n  long num_threads;\n  long itrealvalue;\n  long starttime;\n\n  long vsizeKb;\n  long rssKb;\n  long rsslim;\n};\n\nstatic_assert(std::is_pod<StatData>::value, \"StatData should be POD.\");\n\nclass Procmon : noncopyable\n{\n public:\n  Procmon(EventLoop* loop, pid_t pid, uint16_t port, const char* procname)\n    : kClockTicksPerSecond_(muduo::ProcessInfo::clockTicksPerSecond()),\n      kbPerPage_(muduo::ProcessInfo::pageSize() / 1024),\n      kBootTime_(getBootTime()),\n      pid_(pid),\n      server_(loop, InetAddress(port), getName()),\n      procname_(procname ? procname : ProcessInfo::procname(readProcFile(\"stat\")).as_string()),\n      hostname_(ProcessInfo::hostname()),\n      cmdline_(getCmdLine()),\n      ticks_(0),\n      cpu_usage_(600 / kPeriod_),  // 10 minutes\n      cpu_chart_(640, 100, 600, kPeriod_),\n      ram_chart_(640, 100, 7200, 30)\n  {\n    {\n    // chdir to the same cwd of the process being monitored.\n    string cwd = readLink(\"cwd\");\n    if (::chdir(cwd.c_str()))\n    {\n      LOG_SYSERR << \"Cannot chdir() to \" << cwd;\n    }\n    }\n\n    {\n    char cwd[1024];\n    if (::getcwd(cwd, sizeof cwd))\n    {\n      LOG_INFO << \"Current dir: \" << cwd;\n    }\n    }\n    memZero(&lastStatData_, sizeof lastStatData_);\n    server_.setHttpCallback(std::bind(&Procmon::onRequest, this, _1, _2));\n  }\n\n  void start()\n  {\n    tick();\n    server_.getLoop()->runEvery(kPeriod_, std::bind(&Procmon::tick, this));\n    server_.start();\n  }\n\n private:\n\n  string getName() const\n  {\n    char name[256];\n    snprintf(name, sizeof name, \"procmon-%d\", pid_);\n    return name;\n  }\n\n  void onRequest(const HttpRequest& req, HttpResponse* resp)\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"text/plain\");\n    resp->addHeader(\"Server\", \"Muduo-Procmon\");\n\n    /*\n    if (!processExists(pid_))\n    {\n      resp->setStatusCode(HttpResponse::k404NotFound);\n      resp->setStatusMessage(\"Not Found\");\n      resp->setCloseConnection(true);\n      return;\n    }\n    */\n\n    if (req.path() == \"/\")\n    {\n      resp->setContentType(\"text/html\");\n      fillOverview(req.query());\n      resp->setBody(response_.retrieveAllAsString());\n    }\n    else if (req.path() == \"/cmdline\")\n    {\n      resp->setBody(cmdline_);\n    }\n    else if (req.path() == \"/cpu.png\")\n    {\n      std::vector<double> cpu_usage;\n      for (size_t i = 0; i < cpu_usage_.size(); ++i)\n        cpu_usage.push_back(cpu_usage_[i].cpuUsage(kPeriod_, kClockTicksPerSecond_));\n      string png = cpu_chart_.plotCpu(cpu_usage);\n      resp->setContentType(\"image/png\");\n      resp->setBody(png);\n    }\n    // FIXME: replace with a map\n    else if (req.path() == \"/environ\")\n    {\n      resp->setBody(getEnviron());\n    }\n    else if (req.path() == \"/io\")\n    {\n      resp->setBody(readProcFile(\"io\"));\n    }\n    else if (req.path() == \"/limits\")\n    {\n      resp->setBody(readProcFile(\"limits\"));\n    }\n    else if (req.path() == \"/maps\")\n    {\n      resp->setBody(readProcFile(\"maps\"));\n    }\n    // numa_maps\n    else if (req.path() == \"/smaps\")\n    {\n      resp->setBody(readProcFile(\"smaps\"));\n    }\n    else if (req.path() == \"/status\")\n    {\n      resp->setBody(readProcFile(\"status\"));\n    }\n    else if (req.path() == \"/files\")\n    {\n      listFiles();\n      resp->setBody(response_.retrieveAllAsString());\n    }\n    else if (req.path() == \"/threads\")\n    {\n      listThreads();\n      resp->setBody(response_.retrieveAllAsString());\n    }\n    else\n    {\n      resp->setStatusCode(HttpResponse::k404NotFound);\n      resp->setStatusMessage(\"Not Found\");\n      resp->setCloseConnection(true);\n    }\n  }\n\n  void fillOverview(const string& query)\n  {\n    response_.retrieveAll();\n    Timestamp now = Timestamp::now();\n    appendResponse(\"<html><head><title>%s on %s</title>\\n\",\n                   procname_.c_str(), hostname_.c_str());\n    fillRefresh(query);\n    appendResponse(\"</head><body>\\n\");\n\n    string stat = readProcFile(\"stat\");\n    if (stat.empty())\n    {\n      appendResponse(\"<h1>PID %d doesn't exist.</h1></body></html>\", pid_);\n      return;\n    }\n    int pid = atoi(stat.c_str());\n    assert(pid == pid_);\n    StringPiece procname = ProcessInfo::procname(stat);\n    appendResponse(\"<h1>%s on %s</h1>\\n\",\n                   procname.as_string().c_str(), hostname_.c_str());\n    response_.append(\"<p>Refresh <a href=\\\"?refresh=1\\\">1s</a> \");\n    response_.append(\"<a href=\\\"?refresh=2\\\">2s</a> \");\n    response_.append(\"<a href=\\\"?refresh=5\\\">5s</a> \");\n    response_.append(\"<a href=\\\"?refresh=15\\\">15s</a> \");\n    response_.append(\"<a href=\\\"?refresh=60\\\">60s</a>\\n\");\n    response_.append(\"<p><a href=\\\"/cmdline\\\">Command line</a>\\n\");\n    response_.append(\"<a href=\\\"/environ\\\">Environment variables</a>\\n\");\n    response_.append(\"<a href=\\\"/threads\\\">Threads</a>\\n\");\n\n    appendResponse(\"<p>Page generated at %s (UTC)\", now.toFormattedString().c_str());\n\n    response_.append(\"<p><table>\");\n    StatData statData;  // how about use lastStatData_ ?\n    memZero(&statData, sizeof statData);\n    statData.parse(procname.end()+1, kbPerPage_);  // end is ')'\n\n    appendTableRow(\"PID\", pid);\n    Timestamp started(getStartTime(statData.starttime));  // FIXME: cache it;\n    appendTableRow(\"Started at\", started.toFormattedString(false /*showMicroseconds*/) + \" (UTC)\");\n    appendTableRowFloat(\"Uptime (s)\", timeDifference(now, started));  // FIXME: format as days+H:M:S\n    appendTableRow(\"Executable\", readLink(\"exe\"));\n    appendTableRow(\"Current dir\", readLink(\"cwd\"));\n\n    appendTableRow(\"State\", getState(statData.state));\n    appendTableRowFloat(\"User time (s)\", getSeconds(statData.utime));\n    appendTableRowFloat(\"System time (s)\", getSeconds(statData.stime));\n\n    appendTableRow(\"VmSize (KiB)\", statData.vsizeKb);\n    appendTableRow(\"VmRSS (KiB)\", statData.rssKb);\n    appendTableRow(\"Threads\", statData.num_threads);\n    appendTableRow(\"CPU usage\", \"<img src=\\\"/cpu.png\\\" height=\\\"100\\\" witdh=\\\"640\\\">\");\n\n    appendTableRow(\"Priority\", statData.priority);\n    appendTableRow(\"Nice\", statData.nice);\n\n    appendTableRow(\"Minor page faults\", statData.minflt);\n    appendTableRow(\"Major page faults\", statData.majflt);\n    // TODO: user\n\n    response_.append(\"</table>\");\n    response_.append(\"</body></html>\");\n  }\n\n  void fillRefresh(const string& query)\n  {\n    size_t p = query.find(\"refresh=\");\n    if (p != string::npos)\n    {\n      int seconds = atoi(query.c_str()+p+8);\n      if (seconds > 0)\n      {\n        appendResponse(\"<meta http-equiv=\\\"refresh\\\" content=\\\"%d\\\">\\n\", seconds);\n      }\n    }\n  }\n\n  static int dirFilter(const struct dirent* d)\n  {\n    return (d->d_name[0] != '.');\n  }\n\n  static char getDirType(char d_type)\n  {\n    switch (d_type)\n    {\n      case DT_REG: return '-';\n      case DT_DIR: return 'd';\n      case DT_LNK: return 'l';\n      default: return '?';\n    }\n  }\n\n  void listFiles()\n  {\n    struct dirent** namelist = NULL;\n    int result = ::scandir(\".\", &namelist, dirFilter, alphasort);\n    for (int i = 0; i < result; ++i)\n    {\n      struct stat stat;\n      if (::lstat(namelist[i]->d_name, &stat) == 0)\n      {\n        Timestamp mtime(stat.st_mtime * Timestamp::kMicroSecondsPerSecond);\n        int64_t size = stat.st_size;\n        appendResponse(\"%c %9\" PRId64 \" %s %s\", getDirType(namelist[i]->d_type), size,\n                       mtime.toFormattedString(/*showMicroseconds=*/false).c_str(),\n                       namelist[i]->d_name);\n        if (namelist[i]->d_type == DT_LNK)\n        {\n          char link[1024];\n          ssize_t len = ::readlink(namelist[i]->d_name, link, sizeof link - 1);\n          if (len > 0)\n          {\n            link[len] = '\\0';\n            appendResponse(\" -> %s\", link);\n          }\n        }\n        appendResponse(\"\\n\");\n      }\n      ::free(namelist[i]);\n    }\n    ::free(namelist);\n  }\n\n  void listThreads()\n  {\n    response_.retrieveAll();\n    // FIXME: implement this\n  }\n\n  string readProcFile(const char* basename)\n  {\n    char filename[256];\n    snprintf(filename, sizeof filename, \"/proc/%d/%s\", pid_, basename);\n    string content;\n    FileUtil::readFile(filename, 1024*1024, &content);\n    return content;\n  }\n\n  string readLink(const char* basename)\n  {\n    char filename[256];\n    snprintf(filename, sizeof filename, \"/proc/%d/%s\", pid_, basename);\n    char link[1024];\n    ssize_t len = ::readlink(filename, link, sizeof link);\n    string result;\n    if (len > 0)\n    {\n      result.assign(link, len);\n    }\n    return result;\n  }\n\n  int appendResponse(const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));\n\n  void appendTableRow(const char* name, long value)\n  {\n    appendResponse(\"<tr><td>%s</td><td>%ld</td></tr>\\n\", name, value);\n  }\n\n  void appendTableRowFloat(const char* name, double value)\n  {\n    appendResponse(\"<tr><td>%s</td><td>%.2f</td></tr>\\n\", name, value);\n  }\n\n  void appendTableRow(const char* name, StringArg value)\n  {\n    appendResponse(\"<tr><td>%s</td><td>%s</td></tr>\\n\", name, value.c_str());\n  }\n\n  string getCmdLine()\n  {\n    return boost::replace_all_copy(readProcFile(\"cmdline\"), string(1, '\\0'), \"\\n\\t\");\n  }\n\n  string getEnviron()\n  {\n    return boost::replace_all_copy(readProcFile(\"environ\"), string(1, '\\0'), \"\\n\");\n  }\n\n  Timestamp getStartTime(long starttime)\n  {\n    return Timestamp(Timestamp::kMicroSecondsPerSecond * kBootTime_\n                     + Timestamp::kMicroSecondsPerSecond * starttime / kClockTicksPerSecond_);\n  }\n\n  double getSeconds(long ticks)\n  {\n    return static_cast<double>(ticks) / kClockTicksPerSecond_;\n  }\n\n  void tick()\n  {\n    string stat = readProcFile(\"stat\");  // FIXME: catch file descriptor\n    if (stat.empty())\n      return;\n    StringPiece procname = ProcessInfo::procname(stat);\n    StatData statData;\n    memZero(&statData, sizeof statData);\n    statData.parse(procname.end()+1, kbPerPage_);  // end is ')'\n    if (ticks_ > 0)\n    {\n      CpuTime time;\n      time.userTime_ = std::max(0, static_cast<int>(statData.utime - lastStatData_.utime));\n      time.sysTime_ = std::max(0, static_cast<int>(statData.stime - lastStatData_.stime));\n      cpu_usage_.push_back(time);\n    }\n\n    lastStatData_ = statData;\n    ++ticks_;\n  }\n\n  //\n  // static member functions\n  //\n\n  static const char* getState(char state)\n  {\n    // One character from the string \"RSDZTW\" where R is running, S is sleeping in\n    // an interruptible wait, D is waiting in uninterruptible disk sleep, Z is zombie,\n    // T is traced or stopped (on a signal), and W is paging.\n    switch (state)\n    {\n      case 'R':\n        return \"Running\";\n      case 'S':\n        return \"Sleeping\";\n      case 'D':\n        return \"Disk sleep\";\n      case 'Z':\n        return \"Zombie\";\n      default:\n        return \"Unknown\";\n    }\n  }\n\n  static long getLong(const string& status, const char* key)\n  {\n    long result = 0;\n    size_t pos = status.find(key);\n    if (pos != string::npos)\n    {\n      result = ::atol(status.c_str() + pos + strlen(key));\n    }\n    return result;\n  }\n\n  static long getBootTime()\n  {\n    string stat;\n    FileUtil::readFile(\"/proc/stat\", 65536, &stat);\n    return getLong(stat, \"btime \");\n  }\n\n  struct CpuTime\n  {\n    int userTime_;\n    int sysTime_;\n    double cpuUsage(double kPeriod, double kClockTicksPerSecond) const\n    {\n      return (userTime_ + sysTime_) / (kClockTicksPerSecond * kPeriod);\n    }\n  };\n\n  const static int kPeriod_ = 2.0;\n  const int kClockTicksPerSecond_;\n  const int kbPerPage_;\n  const long kBootTime_;  // in Unix-time\n  const pid_t pid_;\n  HttpServer server_;\n  const string procname_;\n  const string hostname_;\n  const string cmdline_;\n  int ticks_;\n  StatData lastStatData_;\n  boost::circular_buffer<CpuTime> cpu_usage_;\n  Plot cpu_chart_;\n  Plot ram_chart_;\n  // scratch variables\n  Buffer response_;\n};\n\n// define outline for __attribute__\nint Procmon::appendResponse(const char* fmt, ...)\n{\n  char buf[1024];\n  va_list args;\n  va_start(args, fmt);\n  int ret = vsnprintf(buf, sizeof buf, fmt, args);\n  va_end(args);\n  response_.append(buf);\n  return ret;\n}\n\nbool processExists(pid_t pid)\n{\n  char filename[256];\n  snprintf(filename, sizeof filename, \"/proc/%d/stat\", pid);\n  return ::access(filename, R_OK) == 0;\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 3)\n  {\n    printf(\"Usage: %s pid port [name]\\n\", argv[0]);\n    return 0;\n  }\n  int pid = atoi(argv[1]);\n  if (!processExists(pid))\n  {\n    printf(\"Process %d doesn't exist.\\n\", pid);\n    return 1;\n  }\n\n  EventLoop loop;\n  uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n  Procmon procmon(&loop, pid, port, argc > 3 ? argv[3] : NULL);\n  procmon.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/protobuf/CMakeLists.txt",
    "content": "add_subdirectory(codec)\nadd_subdirectory(rpc)\nadd_subdirectory(rpcbalancer)\nadd_subdirectory(rpcbench)\n\nif(CARES_INCLUDE_DIR AND CARES_LIBRARY)\n  add_subdirectory(resolver)\nelse()\n  add_subdirectory(resolver EXCLUDE_FROM_ALL)\nendif()\n"
  },
  {
    "path": "examples/protobuf/codec/CMakeLists.txt",
    "content": "add_library(protobuf_codec codec.cc)\ntarget_link_libraries(protobuf_codec protobuf muduo_net z)\n\nadd_custom_command(OUTPUT query.pb.cc query.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/query.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS query.proto\n  VERBATIM )\n\nset_source_files_properties(query.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion -Wno-shadow\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(query_proto query.pb.cc)\ntarget_link_libraries(query_proto protobuf pthread)\n\nadd_executable(protobuf_codec_test codec_test.cc)\nset_target_properties(protobuf_codec_test PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_codec_test protobuf_codec query_proto)\n\nadd_executable(protobuf_dispatcher_lite_test dispatcher_lite_test.cc)\nset_target_properties(protobuf_dispatcher_lite_test PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_dispatcher_lite_test query_proto)\n\nadd_executable(protobuf_dispatcher_test dispatcher_test.cc)\nset_target_properties(protobuf_dispatcher_test PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_dispatcher_test query_proto)\n\nadd_executable(protobuf_server server.cc)\nset_target_properties(protobuf_server PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_server protobuf_codec query_proto)\n\nadd_executable(protobuf_client client.cc)\nset_target_properties(protobuf_client PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_client protobuf_codec query_proto)\n\nadd_custom_target(protobuf_codec_all\n                  DEPENDS\n                        protobuf_codec_test\n                        protobuf_dispatcher_lite_test\n                        protobuf_dispatcher_test\n                        protobuf_server\n                        protobuf_client)\n"
  },
  {
    "path": "examples/protobuf/codec/client.cc",
    "content": "#include \"examples/protobuf/codec/dispatcher.h\"\n#include \"examples/protobuf/codec/codec.h\"\n#include \"examples/protobuf/codec/query.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::shared_ptr<muduo::Empty> EmptyPtr;\ntypedef std::shared_ptr<muduo::Answer> AnswerPtr;\n\ngoogle::protobuf::Message* messageToSend;\n\nclass QueryClient : noncopyable\n{\n public:\n  QueryClient(EventLoop* loop,\n              const InetAddress& serverAddr)\n  : loop_(loop),\n    client_(loop, serverAddr, \"QueryClient\"),\n    dispatcher_(std::bind(&QueryClient::onUnknownMessage, this, _1, _2, _3)),\n    codec_(std::bind(&ProtobufDispatcher::onProtobufMessage, &dispatcher_, _1, _2, _3))\n  {\n    dispatcher_.registerMessageCallback<muduo::Answer>(\n        std::bind(&QueryClient::onAnswer, this, _1, _2, _3));\n    dispatcher_.registerMessageCallback<muduo::Empty>(\n        std::bind(&QueryClient::onEmpty, this, _1, _2, _3));\n    client_.setConnectionCallback(\n        std::bind(&QueryClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&ProtobufCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      codec_.send(conn, *messageToSend);\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\n  void onUnknownMessage(const TcpConnectionPtr&,\n                        const MessagePtr& message,\n                        Timestamp)\n  {\n    LOG_INFO << \"onUnknownMessage: \" << message->GetTypeName();\n  }\n\n  void onAnswer(const muduo::net::TcpConnectionPtr&,\n                const AnswerPtr& message,\n                muduo::Timestamp)\n  {\n    LOG_INFO << \"onAnswer:\\n\" << message->GetTypeName() << message->DebugString();\n  }\n\n  void onEmpty(const muduo::net::TcpConnectionPtr&,\n               const EmptyPtr& message,\n               muduo::Timestamp)\n  {\n    LOG_INFO << \"onEmpty: \" << message->GetTypeName();\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  ProtobufDispatcher dispatcher_;\n  ProtobufCodec codec_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 2)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(argv[1], port);\n\n    muduo::Query query;\n    query.set_id(1);\n    query.set_questioner(\"Chen Shuo\");\n    query.add_question(\"Running?\");\n    muduo::Empty empty;\n    messageToSend = &query;\n\n    if (argc > 3 && argv[3][0] == 'e')\n    {\n      messageToSend = &empty;\n    }\n\n    QueryClient client(&loop, serverAddr);\n    client.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip port [q|e]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/protobuf/codec/codec.cc",
    "content": "// Copyright 2011, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"examples/protobuf/codec/codec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/protorpc/google-inl.h\"\n\n#include <google/protobuf/descriptor.h>\n\n#include <zlib.h>  // adler32\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid ProtobufCodec::fillEmptyBuffer(Buffer* buf, const google::protobuf::Message& message)\n{\n  // buf->retrieveAll();\n  assert(buf->readableBytes() == 0);\n\n  const std::string& typeName = message.GetTypeName();\n  int32_t nameLen = static_cast<int32_t>(typeName.size()+1);\n  buf->appendInt32(nameLen);\n  buf->append(typeName.c_str(), nameLen);\n\n  // code copied from MessageLite::SerializeToArray() and MessageLite::SerializePartialToArray().\n  GOOGLE_DCHECK(message.IsInitialized()) << InitializationErrorMessage(\"serialize\", message);\n\n  /**\n   * 'ByteSize()' of message is deprecated in Protocol Buffers v3.4.0 firstly.\n   * But, till to v3.11.0, it just getting start to be marked by '__attribute__((deprecated()))'.\n   * So, here, v3.9.2 is selected as maximum version using 'ByteSize()' to avoid\n   * potential effect for previous muduo code/projects as far as possible.\n   * Note: All information above just INFER from\n   * 1) https://github.com/protocolbuffers/protobuf/releases/tag/v3.4.0\n   * 2) MACRO in file 'include/google/protobuf/port_def.inc'.\n   * eg. '#define PROTOBUF_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))'.\n   * In addition, usage of 'ToIntSize()' comes from Impl of ByteSize() in new version's Protocol Buffers.\n   */\n\n  #if GOOGLE_PROTOBUF_VERSION > 3009002\n    int byte_size = google::protobuf::internal::ToIntSize(message.ByteSizeLong());\n  #else\n    int byte_size = message.ByteSize();\n  #endif\n  buf->ensureWritableBytes(byte_size);\n\n  uint8_t* start = reinterpret_cast<uint8_t*>(buf->beginWrite());\n  uint8_t* end = message.SerializeWithCachedSizesToArray(start);\n  if (end - start != byte_size)\n  {\n    #if GOOGLE_PROTOBUF_VERSION > 3009002\n      ByteSizeConsistencyError(byte_size, google::protobuf::internal::ToIntSize(message.ByteSizeLong()), static_cast<int>(end - start));\n    #else\n      ByteSizeConsistencyError(byte_size, message.ByteSize(), static_cast<int>(end - start));\n    #endif\n  }\n  buf->hasWritten(byte_size);\n\n  int32_t checkSum = static_cast<int32_t>(\n      ::adler32(1,\n                reinterpret_cast<const Bytef*>(buf->peek()),\n                static_cast<int>(buf->readableBytes())));\n  buf->appendInt32(checkSum);\n  assert(buf->readableBytes() == sizeof nameLen + nameLen + byte_size + sizeof checkSum);\n  int32_t len = sockets::hostToNetwork32(static_cast<int32_t>(buf->readableBytes()));\n  buf->prepend(&len, sizeof len);\n}\n\n//\n// no more google code after this\n//\n\n//\n// FIXME: merge with RpcCodec\n//\n\nnamespace\n{\n  const string kNoErrorStr = \"NoError\";\n  const string kInvalidLengthStr = \"InvalidLength\";\n  const string kCheckSumErrorStr = \"CheckSumError\";\n  const string kInvalidNameLenStr = \"InvalidNameLen\";\n  const string kUnknownMessageTypeStr = \"UnknownMessageType\";\n  const string kParseErrorStr = \"ParseError\";\n  const string kUnknownErrorStr = \"UnknownError\";\n}\n\nconst string& ProtobufCodec::errorCodeToString(ErrorCode errorCode)\n{\n  switch (errorCode)\n  {\n   case kNoError:\n     return kNoErrorStr;\n   case kInvalidLength:\n     return kInvalidLengthStr;\n   case kCheckSumError:\n     return kCheckSumErrorStr;\n   case kInvalidNameLen:\n     return kInvalidNameLenStr;\n   case kUnknownMessageType:\n     return kUnknownMessageTypeStr;\n   case kParseError:\n     return kParseErrorStr;\n   default:\n     return kUnknownErrorStr;\n  }\n}\n\nvoid ProtobufCodec::defaultErrorCallback(const muduo::net::TcpConnectionPtr& conn,\n                                         muduo::net::Buffer* buf,\n                                         muduo::Timestamp,\n                                         ErrorCode errorCode)\n{\n  LOG_ERROR << \"ProtobufCodec::defaultErrorCallback - \" << errorCodeToString(errorCode);\n  if (conn && conn->connected())\n  {\n    conn->shutdown();\n  }\n}\n\nint32_t asInt32(const char* buf)\n{\n  int32_t be32 = 0;\n  ::memcpy(&be32, buf, sizeof(be32));\n  return sockets::networkToHost32(be32);\n}\n\nvoid ProtobufCodec::onMessage(const TcpConnectionPtr& conn,\n                              Buffer* buf,\n                              Timestamp receiveTime)\n{\n  while (buf->readableBytes() >= kMinMessageLen + kHeaderLen)\n  {\n    const int32_t len = buf->peekInt32();\n    if (len > kMaxMessageLen || len < kMinMessageLen)\n    {\n      errorCallback_(conn, buf, receiveTime, kInvalidLength);\n      break;\n    }\n    else if (buf->readableBytes() >= implicit_cast<size_t>(len + kHeaderLen))\n    {\n      ErrorCode errorCode = kNoError;\n      MessagePtr message = parse(buf->peek()+kHeaderLen, len, &errorCode);\n      if (errorCode == kNoError && message)\n      {\n        messageCallback_(conn, message, receiveTime);\n        buf->retrieve(kHeaderLen+len);\n      }\n      else\n      {\n        errorCallback_(conn, buf, receiveTime, errorCode);\n        break;\n      }\n    }\n    else\n    {\n      break;\n    }\n  }\n}\n\ngoogle::protobuf::Message* ProtobufCodec::createMessage(const std::string& typeName)\n{\n  google::protobuf::Message* message = NULL;\n  const google::protobuf::Descriptor* descriptor =\n    google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);\n  if (descriptor)\n  {\n    const google::protobuf::Message* prototype =\n      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);\n    if (prototype)\n    {\n      message = prototype->New();\n    }\n  }\n  return message;\n}\n\nMessagePtr ProtobufCodec::parse(const char* buf, int len, ErrorCode* error)\n{\n  MessagePtr message;\n\n  // check sum\n  int32_t expectedCheckSum = asInt32(buf + len - kHeaderLen);\n  int32_t checkSum = static_cast<int32_t>(\n      ::adler32(1,\n                reinterpret_cast<const Bytef*>(buf),\n                static_cast<int>(len - kHeaderLen)));\n  if (checkSum == expectedCheckSum)\n  {\n    // get message type name\n    int32_t nameLen = asInt32(buf);\n    if (nameLen >= 2 && nameLen <= len - 2*kHeaderLen)\n    {\n      std::string typeName(buf + kHeaderLen, buf + kHeaderLen + nameLen - 1);\n      // create message object\n      message.reset(createMessage(typeName));\n      if (message)\n      {\n        // parse from buffer\n        const char* data = buf + kHeaderLen + nameLen;\n        int32_t dataLen = len - nameLen - 2*kHeaderLen;\n        if (message->ParseFromArray(data, dataLen))\n        {\n          *error = kNoError;\n        }\n        else\n        {\n          *error = kParseError;\n        }\n      }\n      else\n      {\n        *error = kUnknownMessageType;\n      }\n    }\n    else\n    {\n      *error = kInvalidNameLen;\n    }\n  }\n  else\n  {\n    *error = kCheckSumError;\n  }\n\n  return message;\n}\n"
  },
  {
    "path": "examples/protobuf/codec/codec.h",
    "content": "// Copyright 2011, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_EXAMPLES_PROTOBUF_CODEC_CODEC_H\n#define MUDUO_EXAMPLES_PROTOBUF_CODEC_CODEC_H\n\n#include \"muduo/net/Buffer.h\"\n#include \"muduo/net/TcpConnection.h\"\n\n#include <google/protobuf/message.h>\n\n// struct ProtobufTransportFormat __attribute__ ((__packed__))\n// {\n//   int32_t  len;\n//   int32_t  nameLen;\n//   char     typeName[nameLen];\n//   char     protobufData[len-nameLen-8];\n//   int32_t  checkSum; // adler32 of nameLen, typeName and protobufData\n// }\n\ntypedef std::shared_ptr<google::protobuf::Message> MessagePtr;\n\n//\n// FIXME: merge with RpcCodec\n//\nclass ProtobufCodec : muduo::noncopyable\n{\n public:\n\n  enum ErrorCode\n  {\n    kNoError = 0,\n    kInvalidLength,\n    kCheckSumError,\n    kInvalidNameLen,\n    kUnknownMessageType,\n    kParseError,\n  };\n\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                const MessagePtr&,\n                                muduo::Timestamp)> ProtobufMessageCallback;\n\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                muduo::net::Buffer*,\n                                muduo::Timestamp,\n                                ErrorCode)> ErrorCallback;\n\n  explicit ProtobufCodec(const ProtobufMessageCallback& messageCb)\n    : messageCallback_(messageCb),\n      errorCallback_(defaultErrorCallback)\n  {\n  }\n\n  ProtobufCodec(const ProtobufMessageCallback& messageCb, const ErrorCallback& errorCb)\n    : messageCallback_(messageCb),\n      errorCallback_(errorCb)\n  {\n  }\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp receiveTime);\n\n  void send(const muduo::net::TcpConnectionPtr& conn,\n            const google::protobuf::Message& message)\n  {\n    // FIXME: serialize to TcpConnection::outputBuffer()\n    muduo::net::Buffer buf;\n    fillEmptyBuffer(&buf, message);\n    conn->send(&buf);\n  }\n\n  static const muduo::string& errorCodeToString(ErrorCode errorCode);\n  static void fillEmptyBuffer(muduo::net::Buffer* buf, const google::protobuf::Message& message);\n  static google::protobuf::Message* createMessage(const std::string& type_name);\n  static MessagePtr parse(const char* buf, int len, ErrorCode* errorCode);\n\n private:\n  static void defaultErrorCallback(const muduo::net::TcpConnectionPtr&,\n                                   muduo::net::Buffer*,\n                                   muduo::Timestamp,\n                                   ErrorCode);\n\n  ProtobufMessageCallback messageCallback_;\n  ErrorCallback errorCallback_;\n\n  const static int kHeaderLen = sizeof(int32_t);\n  const static int kMinMessageLen = 2*kHeaderLen + 2; // nameLen + typeName + checkSum\n  const static int kMaxMessageLen = 64*1024*1024; // same as codec_stream.h kDefaultTotalBytesLimit\n};\n\n#endif  // MUDUO_EXAMPLES_PROTOBUF_CODEC_CODEC_H\n"
  },
  {
    "path": "examples/protobuf/codec/codec_test.cc",
    "content": "#include \"examples/protobuf/codec/codec.h\"\n#include \"muduo/net/Endian.h\"\n#include \"examples/protobuf/codec/query.pb.h\"\n\n#include <stdio.h>\n#include <zlib.h>  // adler32\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid print(const Buffer& buf)\n{\n  printf(\"encoded to %zd bytes\\n\", buf.readableBytes());\n  for (size_t i = 0; i < buf.readableBytes(); ++i)\n  {\n    unsigned char ch = static_cast<unsigned char>(buf.peek()[i]);\n\n    printf(\"%2zd:  0x%02x  %c\\n\", i, ch, isgraph(ch) ? ch : ' ');\n  }\n}\n\nvoid testQuery()\n{\n  muduo::Query query;\n  query.set_id(1);\n  query.set_questioner(\"Chen Shuo\");\n  query.add_question(\"Running?\");\n\n  Buffer buf;\n  ProtobufCodec::fillEmptyBuffer(&buf, query);\n  print(buf);\n\n  const int32_t len = buf.readInt32();\n  assert(len == static_cast<int32_t>(buf.readableBytes()));\n\n  ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n  MessagePtr message = ProtobufCodec::parse(buf.peek(), len, &errorCode);\n  assert(errorCode == ProtobufCodec::kNoError);\n  assert(message != NULL);\n  message->PrintDebugString();\n  assert(message->DebugString() == query.DebugString());\n\n  std::shared_ptr<muduo::Query> newQuery = down_pointer_cast<muduo::Query>(message);\n  assert(newQuery != NULL);\n}\n\nvoid testAnswer()\n{\n  muduo::Answer answer;\n  answer.set_id(1);\n  answer.set_questioner(\"Chen Shuo\");\n  answer.set_answerer(\"blog.csdn.net/Solstice\");\n  answer.add_solution(\"Jump!\");\n  answer.add_solution(\"Win!\");\n\n  Buffer buf;\n  ProtobufCodec::fillEmptyBuffer(&buf, answer);\n  print(buf);\n\n  const int32_t len = buf.readInt32();\n  assert(len == static_cast<int32_t>(buf.readableBytes()));\n\n  ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n  MessagePtr message = ProtobufCodec::parse(buf.peek(), len, &errorCode);\n  assert(errorCode == ProtobufCodec::kNoError);\n  assert(message != NULL);\n  message->PrintDebugString();\n  assert(message->DebugString() == answer.DebugString());\n\n  std::shared_ptr<muduo::Answer> newAnswer = down_pointer_cast<muduo::Answer>(message);\n  assert(newAnswer != NULL);\n}\n\nvoid testEmpty()\n{\n  muduo::Empty empty;\n\n  Buffer buf;\n  ProtobufCodec::fillEmptyBuffer(&buf, empty);\n  print(buf);\n\n  const int32_t len = buf.readInt32();\n  assert(len == static_cast<int32_t>(buf.readableBytes()));\n\n  ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n  MessagePtr message = ProtobufCodec::parse(buf.peek(), len, &errorCode);\n  assert(message != NULL);\n  message->PrintDebugString();\n  assert(message->DebugString() == empty.DebugString());\n}\n\nvoid redoCheckSum(string& data, int len)\n{\n  int32_t checkSum = sockets::hostToNetwork32(static_cast<int32_t>(\n      ::adler32(1,\n                reinterpret_cast<const Bytef*>(data.c_str()),\n                static_cast<int>(len - 4))));\n  data[len-4] = reinterpret_cast<const char*>(&checkSum)[0];\n  data[len-3] = reinterpret_cast<const char*>(&checkSum)[1];\n  data[len-2] = reinterpret_cast<const char*>(&checkSum)[2];\n  data[len-1] = reinterpret_cast<const char*>(&checkSum)[3];\n}\n\nvoid testBadBuffer()\n{\n  muduo::Empty empty;\n  empty.set_id(43);\n\n  Buffer buf;\n  ProtobufCodec::fillEmptyBuffer(&buf, empty);\n  // print(buf);\n\n  const int32_t len = buf.readInt32();\n  assert(len == static_cast<int32_t>(buf.readableBytes()));\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len-1, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kCheckSumError);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[len-1]++;\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kCheckSumError);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[0]++;\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kCheckSumError);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[3] = 0;\n    redoCheckSum(data, len);\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kInvalidNameLen);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[3] = 100;\n    redoCheckSum(data, len);\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kInvalidNameLen);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[3]--;\n    redoCheckSum(data, len);\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kUnknownMessageType);\n  }\n\n  {\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    data[4] = 'M';\n    redoCheckSum(data, len);\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    assert(message == NULL);\n    assert(errorCode == ProtobufCodec::kUnknownMessageType);\n  }\n\n  {\n    // FIXME: reproduce parse error\n    string data(buf.peek(), len);\n    ProtobufCodec::ErrorCode errorCode = ProtobufCodec::kNoError;\n    redoCheckSum(data, len);\n    MessagePtr message = ProtobufCodec::parse(data.c_str(), len, &errorCode);\n    // assert(message == NULL);\n    // assert(errorCode == ProtobufCodec::kParseError);\n  }\n}\n\nint g_count = 0;\n\nvoid onMessage(const muduo::net::TcpConnectionPtr& conn,\n               const MessagePtr& message,\n               muduo::Timestamp receiveTime)\n{\n  g_count++;\n}\n\nvoid testOnMessage()\n{\n  muduo::Query query;\n  query.set_id(1);\n  query.set_questioner(\"Chen Shuo\");\n  query.add_question(\"Running?\");\n\n  Buffer buf1;\n  ProtobufCodec::fillEmptyBuffer(&buf1, query);\n\n  muduo::Empty empty;\n  empty.set_id(43);\n  empty.set_id(1982);\n\n  Buffer buf2;\n  ProtobufCodec::fillEmptyBuffer(&buf2, empty);\n\n  size_t totalLen = buf1.readableBytes() + buf2.readableBytes();\n  Buffer all;\n  all.append(buf1.peek(), buf1.readableBytes());\n  all.append(buf2.peek(), buf2.readableBytes());\n  assert(all.readableBytes() == totalLen);\n  muduo::net::TcpConnectionPtr conn;\n  muduo::Timestamp t;\n  ProtobufCodec codec(onMessage);\n  for (size_t len = 0; len <= totalLen; ++len)\n  {\n    Buffer input;\n    input.append(all.peek(), len);\n\n    g_count = 0;\n    codec.onMessage(conn, &input, t);\n    int expected = len < buf1.readableBytes() ? 0 : 1;\n    if (len == totalLen) expected = 2;\n    assert(g_count == expected); (void) expected;\n    // printf(\"%2zd %d\\n\", len, g_count);\n\n    input.append(all.peek() + len, totalLen - len);\n    codec.onMessage(conn, &input, t);\n    assert(g_count == 2);\n  }\n}\n\nint main()\n{\n  GOOGLE_PROTOBUF_VERIFY_VERSION;\n\n  testQuery();\n  puts(\"\");\n  testAnswer();\n  puts(\"\");\n  testEmpty();\n  puts(\"\");\n  testBadBuffer();\n  puts(\"\");\n  testOnMessage();\n  puts(\"\");\n\n  puts(\"All pass!!!\");\n\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/codec/dispatcher.h",
    "content": "// Copyright 2011, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_H\n#define MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/net/Callbacks.h\"\n\n#include <google/protobuf/message.h>\n\n#include <map>\n\n#include <type_traits>\n\ntypedef std::shared_ptr<google::protobuf::Message> MessagePtr;\n\nclass Callback : muduo::noncopyable\n{\n public:\n  virtual ~Callback() = default;\n  virtual void onMessage(const muduo::net::TcpConnectionPtr&,\n                         const MessagePtr& message,\n                         muduo::Timestamp) const = 0;\n};\n\ntemplate <typename T>\nclass CallbackT : public Callback\n{\n  static_assert(std::is_base_of<google::protobuf::Message, T>::value,\n                \"T must be derived from gpb::Message.\");\n public:\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                const std::shared_ptr<T>& message,\n                                muduo::Timestamp)> ProtobufMessageTCallback;\n\n  CallbackT(const ProtobufMessageTCallback& callback)\n    : callback_(callback)\n  {\n  }\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 const MessagePtr& message,\n                 muduo::Timestamp receiveTime) const override\n  {\n    std::shared_ptr<T> concrete = muduo::down_pointer_cast<T>(message);\n    assert(concrete != NULL);\n    callback_(conn, concrete, receiveTime);\n  }\n\n private:\n  ProtobufMessageTCallback callback_;\n};\n\nclass ProtobufDispatcher\n{\n public:\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                const MessagePtr& message,\n                                muduo::Timestamp)> ProtobufMessageCallback;\n\n  explicit ProtobufDispatcher(const ProtobufMessageCallback& defaultCb)\n    : defaultCallback_(defaultCb)\n  {\n  }\n\n  void onProtobufMessage(const muduo::net::TcpConnectionPtr& conn,\n                         const MessagePtr& message,\n                         muduo::Timestamp receiveTime) const\n  {\n    CallbackMap::const_iterator it = callbacks_.find(message->GetDescriptor());\n    if (it != callbacks_.end())\n    {\n      it->second->onMessage(conn, message, receiveTime);\n    }\n    else\n    {\n      defaultCallback_(conn, message, receiveTime);\n    }\n  }\n\n  template<typename T>\n  void registerMessageCallback(const typename CallbackT<T>::ProtobufMessageTCallback& callback)\n  {\n    std::shared_ptr<CallbackT<T> > pd(new CallbackT<T>(callback));\n    callbacks_[T::descriptor()] = pd;\n  }\n\n private:\n  typedef std::map<const google::protobuf::Descriptor*, std::shared_ptr<Callback> > CallbackMap;\n\n  CallbackMap callbacks_;\n  ProtobufMessageCallback defaultCallback_;\n};\n#endif  // MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_H\n\n"
  },
  {
    "path": "examples/protobuf/codec/dispatcher_lite.h",
    "content": "// Copyright 2011, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_LITE_H\n#define MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_LITE_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/net/Callbacks.h\"\n\n#include <google/protobuf/message.h>\n\n#include <map>\n\ntypedef std::shared_ptr<google::protobuf::Message> MessagePtr;\n\nclass ProtobufDispatcherLite : muduo::noncopyable\n{\n public:\n  typedef std::function<void (const muduo::net::TcpConnectionPtr&,\n                                const MessagePtr&,\n                                muduo::Timestamp)> ProtobufMessageCallback;\n\n  // ProtobufDispatcher()\n  //   : defaultCallback_(discardProtobufMessage)\n  // {\n  // }\n\n  explicit ProtobufDispatcherLite(const ProtobufMessageCallback& defaultCb)\n    : defaultCallback_(defaultCb)\n  {\n  }\n\n  void onProtobufMessage(const muduo::net::TcpConnectionPtr& conn,\n                         const MessagePtr& message,\n                         muduo::Timestamp receiveTime) const\n  {\n    CallbackMap::const_iterator it = callbacks_.find(message->GetDescriptor());\n    if (it != callbacks_.end())\n    {\n      it->second(conn, message, receiveTime);\n    }\n    else\n    {\n      defaultCallback_(conn, message, receiveTime);\n    }\n  }\n\n  void registerMessageCallback(const google::protobuf::Descriptor* desc,\n                               const ProtobufMessageCallback& callback)\n  {\n    callbacks_[desc] = callback;\n  }\n\n private:\n  // static void discardProtobufMessage(const muduo::net::TcpConnectionPtr&,\n  //                                    const MessagePtr&,\n  //                                    muduo::Timestamp);\n\n  typedef std::map<const google::protobuf::Descriptor*, ProtobufMessageCallback> CallbackMap;\n  CallbackMap callbacks_;\n  ProtobufMessageCallback defaultCallback_;\n};\n\n#endif  // MUDUO_EXAMPLES_PROTOBUF_CODEC_DISPATCHER_LITE_H\n\n"
  },
  {
    "path": "examples/protobuf/codec/dispatcher_lite_test.cc",
    "content": "#include \"examples/protobuf/codec/dispatcher_lite.h\"\n\n#include \"examples/protobuf/codec/query.pb.h\"\n\n#include <iostream>\n\nusing std::cout;\nusing std::endl;\n\nvoid onUnknownMessageType(const muduo::net::TcpConnectionPtr&,\n                          const MessagePtr& message,\n                          muduo::Timestamp)\n{\n  cout << \"onUnknownMessageType: \" << message->GetTypeName() << endl;\n}\n\nvoid onQuery(const muduo::net::TcpConnectionPtr&,\n             const MessagePtr& message,\n             muduo::Timestamp)\n{\n  cout << \"onQuery: \" << message->GetTypeName() << endl;\n  std::shared_ptr<muduo::Query> query = muduo::down_pointer_cast<muduo::Query>(message);\n  assert(query != NULL);\n}\n\nvoid onAnswer(const muduo::net::TcpConnectionPtr&,\n              const MessagePtr& message,\n              muduo::Timestamp)\n{\n  cout << \"onAnswer: \" << message->GetTypeName() << endl;\n  std::shared_ptr<muduo::Answer> answer = muduo::down_pointer_cast<muduo::Answer>(message);\n  assert(answer != NULL);\n}\n\nint main()\n{\n  GOOGLE_PROTOBUF_VERIFY_VERSION;\n\n  ProtobufDispatcherLite dispatcher(onUnknownMessageType);\n  dispatcher.registerMessageCallback(muduo::Query::descriptor(), onQuery);\n  dispatcher.registerMessageCallback(muduo::Answer::descriptor(), onAnswer);\n\n  muduo::net::TcpConnectionPtr conn;\n  muduo::Timestamp t;\n\n  std::shared_ptr<muduo::Query> query(new muduo::Query);\n  std::shared_ptr<muduo::Answer> answer(new muduo::Answer);\n  std::shared_ptr<muduo::Empty> empty(new muduo::Empty);\n  dispatcher.onProtobufMessage(conn, query, t);\n  dispatcher.onProtobufMessage(conn, answer, t);\n  dispatcher.onProtobufMessage(conn, empty, t);\n\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/codec/dispatcher_test.cc",
    "content": "#include \"examples/protobuf/codec/dispatcher.h\"\n\n#include \"examples/protobuf/codec/query.pb.h\"\n\n#include <iostream>\n\nusing std::cout;\nusing std::endl;\n\ntypedef std::shared_ptr<muduo::Query> QueryPtr;\ntypedef std::shared_ptr<muduo::Answer> AnswerPtr;\n\nvoid test_down_pointer_cast()\n{\n  ::std::shared_ptr<google::protobuf::Message> msg(new muduo::Query);\n  ::std::shared_ptr<muduo::Query> query(muduo::down_pointer_cast<muduo::Query>(msg));\n  assert(msg && query);\n  if (!query)\n  {\n    abort();\n  }\n}\n\nvoid onQuery(const muduo::net::TcpConnectionPtr&,\n             const QueryPtr& message,\n             muduo::Timestamp)\n{\n  cout << \"onQuery: \" << message->GetTypeName() << endl;\n}\n\nvoid onAnswer(const muduo::net::TcpConnectionPtr&,\n              const AnswerPtr& message,\n              muduo::Timestamp)\n{\n  cout << \"onAnswer: \" << message->GetTypeName() << endl;\n}\n\nvoid onUnknownMessageType(const muduo::net::TcpConnectionPtr&,\n                          const MessagePtr& message,\n                          muduo::Timestamp)\n{\n  cout << \"onUnknownMessageType: \" << message->GetTypeName() << endl;\n}\n\nint main()\n{\n  GOOGLE_PROTOBUF_VERIFY_VERSION;\n  test_down_pointer_cast();\n\n  ProtobufDispatcher dispatcher(onUnknownMessageType);\n  dispatcher.registerMessageCallback<muduo::Query>(onQuery);\n  dispatcher.registerMessageCallback<muduo::Answer>(onAnswer);\n\n  muduo::net::TcpConnectionPtr conn;\n  muduo::Timestamp t;\n\n  std::shared_ptr<muduo::Query> query(new muduo::Query);\n  std::shared_ptr<muduo::Answer> answer(new muduo::Answer);\n  std::shared_ptr<muduo::Empty> empty(new muduo::Empty);\n  dispatcher.onProtobufMessage(conn, query, t);\n  dispatcher.onProtobufMessage(conn, answer, t);\n  dispatcher.onProtobufMessage(conn, empty, t);\n\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/codec/query.proto",
    "content": "package muduo;\noption java_package = \"muduo.codec.tests\";\noption java_outer_classname = \"QueryProtos\";\n\nmessage Query {\n  required int64 id = 1;\n  required string questioner = 2;\n\n  repeated string question = 3;\n}\n\nmessage Answer {\n  required int64 id = 1;\n  required string questioner = 2;\n  required string answerer = 3;\n\n  repeated string solution = 4;\n}\n\nmessage Empty {\n  optional int32 id = 1;\n}\n"
  },
  {
    "path": "examples/protobuf/codec/server.cc",
    "content": "#include \"examples/protobuf/codec/codec.h\"\n#include \"examples/protobuf/codec/dispatcher.h\"\n#include \"examples/protobuf/codec/query.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::shared_ptr<muduo::Query> QueryPtr;\ntypedef std::shared_ptr<muduo::Answer> AnswerPtr;\n\nclass QueryServer : noncopyable\n{\n public:\n  QueryServer(EventLoop* loop,\n              const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"QueryServer\"),\n    dispatcher_(std::bind(&QueryServer::onUnknownMessage, this, _1, _2, _3)),\n    codec_(std::bind(&ProtobufDispatcher::onProtobufMessage, &dispatcher_, _1, _2, _3))\n  {\n    dispatcher_.registerMessageCallback<muduo::Query>(\n        std::bind(&QueryServer::onQuery, this, _1, _2, _3));\n    dispatcher_.registerMessageCallback<muduo::Answer>(\n        std::bind(&QueryServer::onAnswer, this, _1, _2, _3));\n    server_.setConnectionCallback(\n        std::bind(&QueryServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&ProtobufCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onUnknownMessage(const TcpConnectionPtr& conn,\n                        const MessagePtr& message,\n                        Timestamp)\n  {\n    LOG_INFO << \"onUnknownMessage: \" << message->GetTypeName();\n    conn->shutdown();\n  }\n\n  void onQuery(const muduo::net::TcpConnectionPtr& conn,\n               const QueryPtr& message,\n               muduo::Timestamp)\n  {\n    LOG_INFO << \"onQuery:\\n\" << message->GetTypeName() << message->DebugString();\n    Answer answer;\n    answer.set_id(1);\n    answer.set_questioner(\"Chen Shuo\");\n    answer.set_answerer(\"blog.csdn.net/Solstice\");\n    answer.add_solution(\"Jump!\");\n    answer.add_solution(\"Win!\");\n    codec_.send(conn, answer);\n\n    conn->shutdown();\n  }\n\n  void onAnswer(const muduo::net::TcpConnectionPtr& conn,\n                const AnswerPtr& message,\n                muduo::Timestamp)\n  {\n    LOG_INFO << \"onAnswer: \" << message->GetTypeName();\n    conn->shutdown();\n  }\n\n  TcpServer server_;\n  ProtobufDispatcher dispatcher_;\n  ProtobufCodec codec_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress serverAddr(port);\n    QueryServer server(&loop, serverAddr);\n    server.start();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s port\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/protobuf/resolver/CMakeLists.txt",
    "content": "add_custom_command(OUTPUT resolver.pb.cc resolver.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/resolver.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS resolver.proto)\n\nset_source_files_properties(resolver.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion -Wno-shadow\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(resolver_proto resolver.pb.cc)\ntarget_link_libraries(resolver_proto protobuf pthread)\n\nadd_executable(protobuf_rpc_resolver_client client.cc)\nset_target_properties(protobuf_rpc_resolver_client PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_resolver_client resolver_proto muduo_protorpc)\n\nadd_executable(protobuf_rpc_resolver_server server.cc)\nset_target_properties(protobuf_rpc_resolver_server PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_resolver_server resolver_proto muduo_protorpc muduo_cdns)\n"
  },
  {
    "path": "examples/protobuf/resolver/client.cc",
    "content": "#include \"examples/protobuf/resolver/resolver.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpConnection.h\"\n#include \"muduo/net/protorpc/RpcChannel.h\"\n\n#include <arpa/inet.h>  // inet_ntop\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass RpcClient : noncopyable\n{\n public:\n  RpcClient(EventLoop* loop, const InetAddress& serverAddr)\n    : loop_(loop),\n      client_(loop, serverAddr, \"RpcClient\"),\n      channel_(new RpcChannel),\n      got_(0),\n      total_(0),\n      stub_(get_pointer(channel_))\n  {\n    client_.setConnectionCallback(\n        std::bind(&RpcClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&RpcChannel::onMessage, get_pointer(channel_), _1, _2, _3));\n    // client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      //channel_.reset(new RpcChannel(conn));\n      channel_->setConnection(conn);\n      total_ = 4;\n      resolve(\"www.example.com\");\n      resolve(\"www.chenshuo.com\");\n      resolve(\"www.google.com\");\n      resolve(\"acme.chenshuo.org\");\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\n  void resolve(const std::string& host)\n  {\n    resolver::ResolveRequest request;\n    request.set_address(host);\n    resolver::ResolveResponse* response = new resolver::ResolveResponse;\n\n    stub_.Resolve(NULL, &request, response,\n        NewCallback(this, &RpcClient::resolved, response, host));\n  }\n\n  void resolved(resolver::ResolveResponse* resp, std::string host) // pass by value for NewCallback above\n  {\n    if (resp->resolved())\n    {\n      char buf[32];\n      uint32_t ip = resp->ip(0);\n      inet_ntop(AF_INET, &ip, buf, sizeof buf);\n\n      LOG_INFO << \"resolved \" << host << \" : \" << buf << \"\\n\"\n               << resp->DebugString();\n    }\n    else\n    {\n      LOG_INFO << \"resolved \" << host << \" failed\";\n    }\n\n    if (++got_ >= total_)\n    {\n      client_.disconnect();\n    }\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  RpcChannelPtr channel_;\n  int got_;\n  int total_;\n  resolver::ResolverService::Stub stub_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 2053);\n\n    RpcClient rpcClient(&loop, serverAddr);\n    rpcClient.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/protobuf/resolver/resolver.proto",
    "content": "package resolver;\noption cc_generic_services = true;\noption java_generic_services = true;\noption py_generic_services = true;\n\nmessage ResolveRequest {\n  required string address = 1;\n}\n\nmessage ResolveResponse {\n  optional bool resolved = 1 [default=false];\n  repeated fixed32 ip = 2;\n  repeated int32 port = 3;\n}\n\nservice ResolverService {\n  rpc Resolve (ResolveRequest) returns (ResolveResponse);\n}\n\n"
  },
  {
    "path": "examples/protobuf/resolver/server.cc",
    "content": "#include \"examples/protobuf/resolver/resolver.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/protorpc/RpcServer.h\"\n#include \"examples/cdns/Resolver.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace resolver\n{\n\nclass ResolverServiceImpl : public ResolverService\n{\n public:\n  ResolverServiceImpl(EventLoop* loop)\n    : resolver_(loop, cdns::Resolver::kDNSonly)\n  {\n  }\n\n  virtual void Resolve(::google::protobuf::RpcController* controller,\n                       const ::resolver::ResolveRequest* request,\n                       ::resolver::ResolveResponse* response,\n                       ::google::protobuf::Closure* done)\n  {\n    LOG_INFO << \"ResolverServiceImpl::Resolve \" << request->address();\n\n    bool succeed = resolver_.resolve(request->address(),\n                                     std::bind(&ResolverServiceImpl::doneCallback,\n                                                 this,\n                                                 request->address(),\n                                                 _1,\n                                                 response,\n                                                 done));\n    if (!succeed)\n    {\n      response->set_resolved(false);\n      done->Run();\n    }\n  }\n\n private:\n\n  void doneCallback(const std::string& host,\n                    const muduo::net::InetAddress& address,\n                    ::resolver::ResolveResponse* response,\n                    ::google::protobuf::Closure* done)\n\n  {\n    LOG_INFO << \"ResolverServiceImpl::doneCallback \" << host;\n    int32_t ip = address.ipv4NetEndian();\n    if (ip)\n    {\n      response->set_resolved(true);\n      response->add_ip(ip);\n      response->add_port(address.portNetEndian());\n    }\n    else\n    {\n      response->set_resolved(false);\n    }\n    done->Run();\n  }\n\n  cdns::Resolver resolver_;\n};\n\n}  // namespace resolver\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2053);\n  resolver::ResolverServiceImpl impl(&loop);\n  RpcServer server(&loop, listenAddr);\n  server.registerService(&impl);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpc/CMakeLists.txt",
    "content": "add_custom_command(OUTPUT sudoku.pb.cc sudoku.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/sudoku.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS sudoku.proto)\n\nset_source_files_properties(sudoku.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion -Wno-shadow\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(sudoku_proto sudoku.pb.cc)\ntarget_link_libraries(sudoku_proto protobuf pthread)\n\nadd_executable(protobuf_rpc_sudoku_client client.cc)\nset_target_properties(protobuf_rpc_sudoku_client PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_sudoku_client sudoku_proto muduo_protorpc)\n\nadd_executable(protobuf_rpc_sudoku_server server.cc)\nset_target_properties(protobuf_rpc_sudoku_server PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_sudoku_server sudoku_proto muduo_protorpc)\n\nadd_custom_target(protobuf_rpc_all\n                  DEPENDS\n                        muduo_protorpc\n                        protobuf_rpc_balancer\n                        protobuf_rpc_balancer_raw\n                        protobuf_rpc_echo_client\n                        protobuf_rpc_echo_server\n                        protobuf_rpc_resolver_client\n                        protobuf_rpc_resolver_server\n                        protobuf_rpc_sudoku_client\n                        protobuf_rpc_sudoku_server\n                        )\n"
  },
  {
    "path": "examples/protobuf/rpc/client.cc",
    "content": "#include \"examples/protobuf/rpc/sudoku.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpConnection.h\"\n#include \"muduo/net/protorpc/RpcChannel.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass RpcClient : noncopyable\n{\n public:\n  RpcClient(EventLoop* loop, const InetAddress& serverAddr)\n    : loop_(loop),\n      client_(loop, serverAddr, \"RpcClient\"),\n      channel_(new RpcChannel),\n      stub_(get_pointer(channel_))\n  {\n    client_.setConnectionCallback(\n        std::bind(&RpcClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&RpcChannel::onMessage, get_pointer(channel_), _1, _2, _3));\n    // client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      //channel_.reset(new RpcChannel(conn));\n      channel_->setConnection(conn);\n      sudoku::SudokuRequest request;\n      request.set_checkerboard(\"001010\");\n      sudoku::SudokuResponse* response = new sudoku::SudokuResponse;\n\n      stub_.Solve(NULL, &request, response, NewCallback(this, &RpcClient::solved, response));\n    }\n    else\n    {\n      loop_->quit();\n    }\n  }\n\n  void solved(sudoku::SudokuResponse* resp)\n  {\n    LOG_INFO << \"solved:\\n\" << resp->DebugString();\n    client_.disconnect();\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n  RpcChannelPtr channel_;\n  sudoku::SudokuService::Stub stub_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 9981);\n\n    RpcClient rpcClient(&loop, serverAddr);\n    rpcClient.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip\\n\", argv[0]);\n  }\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpc/server.cc",
    "content": "#include \"examples/protobuf/rpc/sudoku.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/protorpc/RpcServer.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace sudoku\n{\n\nclass SudokuServiceImpl : public SudokuService\n{\n public:\n  virtual void Solve(::google::protobuf::RpcController* controller,\n                       const ::sudoku::SudokuRequest* request,\n                       ::sudoku::SudokuResponse* response,\n                       ::google::protobuf::Closure* done)\n  {\n    LOG_INFO << \"SudokuServiceImpl::Solve\";\n    response->set_solved(true);\n    response->set_checkerboard(\"1234567\");\n    done->Run();\n  }\n};\n\n}  // namespace sudoku\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  sudoku::SudokuServiceImpl impl;\n  RpcServer server(&loop, listenAddr);\n  server.registerService(&impl);\n  server.start();\n  loop.loop();\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpc/sudoku.proto",
    "content": "package sudoku;\noption cc_generic_services = true;\noption java_generic_services = true;\noption py_generic_services = true;\n\nmessage SudokuRequest {\n  required string checkerboard = 1;\n}\n\nmessage SudokuResponse {\n  optional bool solved = 1 [default=false];\n  optional string checkerboard = 2;\n}\n\nservice SudokuService {\n  rpc Solve (SudokuRequest) returns (SudokuResponse);\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpcbalancer/CMakeLists.txt",
    "content": "include_directories(${PROJECT_BINARY_DIR})\n\nadd_executable(protobuf_rpc_balancer balancer.cc)\nset_target_properties(protobuf_rpc_balancer PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_balancer muduo_protorpc)\n\nadd_executable(protobuf_rpc_balancer_raw balancer_raw.cc)\nset_target_properties(protobuf_rpc_balancer_raw PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_balancer_raw muduo_protorpc)\n"
  },
  {
    "path": "examples/protobuf/rpcbalancer/balancer.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/base/ThreadLocal.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"muduo/net/protorpc/RpcCodec.h\"\n#include \"muduo/net/protorpc/rpc.pb.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass BackendSession : noncopyable\n{\n public:\n  BackendSession(EventLoop* loop, const InetAddress& backendAddr, const string& name)\n    : loop_(loop),\n      client_(loop, backendAddr, name),\n      codec_(std::bind(&BackendSession::onRpcMessage, this, _1, _2, _3)),\n      nextId_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&BackendSession::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&RpcCodec::onMessage, &codec_, _1, _2, _3));\n    client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  // FIXME: add health check\n  bool send(RpcMessage& msg, const TcpConnectionPtr& clientConn)\n  {\n    loop_->assertInLoopThread();\n    if (conn_)\n    {\n      uint64_t id = ++nextId_;\n      Request r = { msg.id(), clientConn };\n      assert(outstandings_.find(id) == outstandings_.end());\n      outstandings_[id] = r;\n      msg.set_id(id);\n      codec_.send(conn_, msg);\n      // LOG_DEBUG << \"forward \" << r.origId << \" from \" << clientConn->name()\n      //           << \" as \" << id << \" to \" << conn_->name();\n      return true;\n    }\n    else\n      return false;\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    loop_->assertInLoopThread();\n    LOG_INFO << \"Backend \"\n             << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      conn_ = conn;\n    }\n    else\n    {\n      conn_.reset();\n      // FIXME: reject pending\n    }\n  }\n\n  void onRpcMessage(const TcpConnectionPtr&,\n                    const RpcMessagePtr& msg,\n                    Timestamp)\n  {\n    loop_->assertInLoopThread();\n    std::map<uint64_t, Request>::iterator it = outstandings_.find(msg->id());\n    if (it != outstandings_.end())\n    {\n      uint64_t origId = it->second.origId;\n      TcpConnectionPtr clientConn = it->second.clientConn.lock();\n      outstandings_.erase(it);\n\n      if (clientConn)\n      {\n        // LOG_DEBUG << \"send back \" << origId << \" of \" << clientConn->name()\n        //           << \" using \" << msg.id() << \" from \" << conn_->name();\n        msg->set_id(origId);\n        codec_.send(clientConn, *msg);\n      }\n    }\n    else\n    {\n      // LOG_ERROR\n    }\n  }\n\n  struct Request\n  {\n    uint64_t origId;\n    std::weak_ptr<TcpConnection> clientConn;\n  };\n\n  EventLoop* loop_;\n  TcpClient client_;\n  RpcCodec codec_;\n  TcpConnectionPtr conn_;\n  uint64_t nextId_;\n  std::map<uint64_t, Request> outstandings_;\n};\n\nclass Balancer : noncopyable\n{\n public:\n  Balancer(EventLoop* loop,\n           const InetAddress& listenAddr,\n           const string& name,\n           const std::vector<InetAddress>& backends)\n    : server_(loop, listenAddr, name),\n      codec_(std::bind(&Balancer::onRpcMessage, this, _1, _2, _3)),\n      backends_(backends)\n  {\n    server_.setThreadInitCallback(\n        std::bind(&Balancer::initPerThread, this, _1));\n    server_.setConnectionCallback(\n        std::bind(&Balancer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&RpcCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  ~Balancer()\n  {\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  struct PerThread\n  {\n    size_t current;\n    std::vector<std::unique_ptr<BackendSession>> backends;\n    PerThread() : current(0) { }\n  };\n\n  void initPerThread(EventLoop* ioLoop)\n  {\n    int count = threadCount_.getAndAdd(1);\n    LOG_INFO << \"IO thread \" << count;\n    PerThread& t = t_backends_.value();\n    t.current = count % backends_.size();\n\n    for (size_t i = 0; i < backends_.size(); ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"%s#%d\", backends_[i].toIpPort().c_str(), count);\n      t.backends.emplace_back(new BackendSession(ioLoop, backends_[i], buf));\n      t.backends.back()->connect();\n    }\n  }\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << \"Client \"\n             << conn->peerAddress().toIpPort() << \" -> \"\n             << conn->localAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (!conn->connected())\n    {\n      // FIXME: cancel outstanding calls, otherwise, memory leak\n    }\n  }\n\n  void onRpcMessage(const TcpConnectionPtr& conn,\n                    const RpcMessagePtr& msg,\n                    Timestamp)\n  {\n    PerThread& t = t_backends_.value();\n    bool succeed = false;\n    for (size_t i = 0; i < t.backends.size() && !succeed; ++i)\n    {\n      succeed = t.backends[t.current]->send(*msg, conn);\n      t.current = (t.current+1) % t.backends.size();\n    }\n    if (!succeed)\n    {\n      // FIXME: no backend available\n    }\n  }\n\n  TcpServer server_;\n  RpcCodec codec_;\n  std::vector<InetAddress> backends_;\n  AtomicInt32 threadCount_;\n  ThreadLocal<PerThread> t_backends_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc < 3)\n  {\n    fprintf(stderr, \"Usage: %s listen_port backend_ip:port [backend_ip:port]\\n\", argv[0]);\n  }\n  else\n  {\n    std::vector<InetAddress> backends;\n    for (int i = 2; i < argc; ++i)\n    {\n      string hostport = argv[i];\n      size_t colon = hostport.find(':');\n      if (colon != string::npos)\n      {\n        string ip = hostport.substr(0, colon);\n        uint16_t port = static_cast<uint16_t>(atoi(hostport.c_str()+colon+1));\n        backends.push_back(InetAddress(ip, port));\n      }\n      else\n      {\n        fprintf(stderr, \"invalid backend address %s\\n\", argv[i]);\n        return 1;\n      }\n    }\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress listenAddr(port);\n\n    EventLoop loop;\n    Balancer balancer(&loop, listenAddr, \"RpcBalancer\", backends);\n    balancer.setThreadNum(4);\n    balancer.start();\n    loop.loop();\n  }\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpcbalancer/balancer_raw.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/base/ThreadLocal.h\"\n#include \"muduo/net/EventLoop.h\"\n//#include <muduo/net/EventLoopThread.h>\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n//#include <muduo/net/inspect/Inspector.h>\n#include \"muduo/net/protorpc/RpcCodec.h\"\n#include \"muduo/net/protorpc/rpc.pb.h\"\n\n#include <endian.h>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstruct RawMessage\n{\n  RawMessage(StringPiece m)\n    : message_(m), id_(0), loc_(NULL)\n  { }\n\n  uint64_t id() const { return id_; }\n  void set_id(uint64_t x) { id_ = x; }\n\n  bool parse(const string& tag)\n  {\n    const char* const body = message_.data() + ProtobufCodecLite::kHeaderLen;\n    const int bodylen = message_.size() - ProtobufCodecLite::kHeaderLen;\n    const int taglen = static_cast<int>(tag.size());\n    if (ProtobufCodecLite::validateChecksum(body, bodylen)\n        && (memcmp(body, tag.data(), tag.size()) == 0)\n        && (bodylen >= taglen + 3 + 8))\n    {\n      const char* const p = body + taglen;\n      uint8_t type = *(p+1);\n\n      if (*p == 0x08 && (type == 0x01 || type == 0x02) && *(p+2) == 0x11)\n      {\n        uint64_t x = 0;\n        memcpy(&x, p+3, sizeof(x));\n        set_id(le64toh(x));\n        loc_ = p+3;\n        return true;\n      }\n    }\n    return false;\n  }\n\n  void updateId()\n  {\n    uint64_t le64 = htole64(id_);\n    memcpy(const_cast<void*>(loc_), &le64, sizeof(le64));\n\n    const char* body = message_.data() + ProtobufCodecLite::kHeaderLen;\n    int bodylen = message_.size() - ProtobufCodecLite::kHeaderLen;\n    int32_t checkSum = ProtobufCodecLite::checksum(body, bodylen - ProtobufCodecLite::kChecksumLen);\n    int32_t be32 = sockets::hostToNetwork32(checkSum);\n    memcpy(const_cast<char*>(body + bodylen - ProtobufCodecLite::kChecksumLen), &be32, sizeof(be32));\n  }\n\n  StringPiece message_;\n\n private:\n  uint64_t id_;\n  const void* loc_;\n};\n\nclass BackendSession : noncopyable\n{\n public:\n  BackendSession(EventLoop* loop, const InetAddress& backendAddr, const string& name)\n    : loop_(loop),\n      client_(loop, backendAddr, name),\n      codec_(std::bind(&BackendSession::onRpcMessage, this, _1, _2, _3),\n             std::bind(&BackendSession::onRawMessage, this, _1, _2, _3)),\n      nextId_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&BackendSession::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&RpcCodec::onMessage, &codec_, _1, _2, _3));\n    client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  // FIXME: add health check\n  template<typename MSG>\n  bool send(MSG& msg, const TcpConnectionPtr& clientConn)\n  {\n    loop_->assertInLoopThread();\n    if (conn_)\n    {\n      uint64_t id = ++nextId_;\n      Request r = { msg.id(), clientConn };\n      assert(outstandings_.find(id) == outstandings_.end());\n      outstandings_[id] = r;\n      msg.set_id(id);\n      sendTo(conn_, msg);\n      // LOG_DEBUG << \"forward \" << r.origId << \" from \" << clientConn->name()\n      //           << \" as \" << id << \" to \" << conn_->name();\n      return true;\n    }\n    else\n      return false;\n  }\n\n private:\n  void sendTo(const TcpConnectionPtr& conn, const RpcMessage& msg)\n  {\n    codec_.send(conn, msg);\n  }\n\n  void sendTo(const TcpConnectionPtr& conn, RawMessage& msg)\n  {\n    msg.updateId();\n    conn->send(msg.message_);\n  }\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    loop_->assertInLoopThread();\n    LOG_INFO << \"Backend \"\n             << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      conn_ = conn;\n    }\n    else\n    {\n      conn_.reset();\n      // FIXME: reject pending\n    }\n  }\n\n  void onRpcMessage(const TcpConnectionPtr&,\n                    const RpcMessagePtr& msg,\n                    Timestamp)\n  {\n    onMessageT(*msg);\n  }\n\n  bool onRawMessage(const TcpConnectionPtr&,\n                    StringPiece message,\n                    Timestamp)\n  {\n    RawMessage raw(message);\n    if (raw.parse(codec_.tag()))\n    {\n      onMessageT(raw);\n      return false;\n    }\n    else\n      return true; // try normal rpc message callback\n  }\n\n  template<typename MSG>\n  void onMessageT(MSG& msg)\n  {\n    loop_->assertInLoopThread();\n    std::map<uint64_t, Request>::iterator it = outstandings_.find(msg.id());\n    if (it != outstandings_.end())\n    {\n      uint64_t origId = it->second.origId;\n      TcpConnectionPtr clientConn = it->second.clientConn.lock();\n      outstandings_.erase(it);\n\n      if (clientConn)\n      {\n        // LOG_DEBUG << \"send back \" << origId << \" of \" << clientConn->name()\n        //           << \" using \" << msg.id() << \" from \" << conn_->name();\n        msg.set_id(origId);\n        sendTo(clientConn, msg);\n      }\n    }\n    else\n    {\n      // LOG_ERROR\n    }\n  }\n\n  struct Request\n  {\n    uint64_t origId;\n    std::weak_ptr<TcpConnection> clientConn;\n  };\n\n  EventLoop* loop_;\n  TcpClient client_;\n  RpcCodec codec_;\n  TcpConnectionPtr conn_;\n  uint64_t nextId_;\n  std::map<uint64_t, Request> outstandings_;\n};\n\nclass Balancer : noncopyable\n{\n public:\n  Balancer(EventLoop* loop,\n           const InetAddress& listenAddr,\n           const string& name,\n           const std::vector<InetAddress>& backends)\n    : server_(loop, listenAddr, name),\n      codec_(std::bind(&Balancer::onRpcMessage, this, _1, _2, _3),\n             std::bind(&Balancer::onRawMessage, this, _1, _2, _3)),\n      backends_(backends)\n  {\n    server_.setThreadInitCallback(\n        std::bind(&Balancer::initPerThread, this, _1));\n    server_.setConnectionCallback(\n        std::bind(&Balancer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&RpcCodec::onMessage, &codec_, _1, _2, _3));\n  }\n\n  ~Balancer()\n  {\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  struct PerThread\n  {\n    size_t current;\n    std::vector<std::unique_ptr<BackendSession>> backends;\n    PerThread() : current(0) { }\n  };\n\n  void initPerThread(EventLoop* ioLoop)\n  {\n    int count = threadCount_.getAndAdd(1);\n    LOG_INFO << \"IO thread \" << count;\n    PerThread& t = t_backends_.value();\n    t.current = count % backends_.size();\n\n    for (size_t i = 0; i < backends_.size(); ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"%s#%d\", backends_[i].toIpPort().c_str(), count);\n      t.backends.emplace_back(new BackendSession(ioLoop, backends_[i], buf));\n      t.backends.back()->connect();\n    }\n  }\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << \"Client \"\n             << conn->peerAddress().toIpPort() << \" -> \"\n             << conn->localAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (!conn->connected())\n    {\n      // FIXME: cancel outstanding calls, otherwise, memory leak\n    }\n  }\n\n  bool onRawMessage(const TcpConnectionPtr& conn,\n                    StringPiece message,\n                    Timestamp)\n  {\n    RawMessage raw(message);\n    if (raw.parse(codec_.tag()))\n    {\n      onMessageT(conn, raw);\n      return false;\n    }\n    else\n      return true; // try normal rpc message callback\n  }\n\n  void onRpcMessage(const TcpConnectionPtr& conn,\n                    const RpcMessagePtr& msg,\n                    Timestamp)\n  {\n    onMessageT(conn, *msg);\n  }\n\n  template<typename MSG>\n  bool onMessageT(const TcpConnectionPtr& conn, MSG& msg)\n  {\n    PerThread& t = t_backends_.value();\n    bool succeed = false;\n    for (size_t i = 0; i < t.backends.size() && !succeed; ++i)\n    {\n      succeed = t.backends[t.current]->send(msg, conn);\n      t.current = (t.current+1) % t.backends.size();\n    }\n    if (!succeed)\n    {\n      // FIXME: no backend available\n    }\n    return succeed;\n  }\n\n  TcpServer server_;\n  RpcCodec codec_;\n  std::vector<InetAddress> backends_;\n  AtomicInt32 threadCount_;\n  ThreadLocal<PerThread> t_backends_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc < 3)\n  {\n    fprintf(stderr, \"Usage: %s listen_port backend_ip:port [backend_ip:port]\\n\", argv[0]);\n  }\n  else\n  {\n    std::vector<InetAddress> backends;\n    for (int i = 2; i < argc; ++i)\n    {\n      string hostport = argv[i];\n      size_t colon = hostport.find(':');\n      if (colon != string::npos)\n      {\n        string ip = hostport.substr(0, colon);\n        uint16_t port = static_cast<uint16_t>(atoi(hostport.c_str()+colon+1));\n        backends.push_back(InetAddress(ip, port));\n      }\n      else\n      {\n        fprintf(stderr, \"invalid backend address %s\\n\", argv[i]);\n        return 1;\n      }\n    }\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress listenAddr(port);\n\n    // EventLoopThread inspectThread;\n    // new Inspector(inspectThread.startLoop(), InetAddress(8080), \"rpcbalancer\");\n    EventLoop loop;\n    Balancer balancer(&loop, listenAddr, \"RpcBalancer\", backends);\n    balancer.setThreadNum(4);\n    balancer.start();\n    loop.loop();\n  }\n  google::protobuf::ShutdownProtobufLibrary();\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpcbench/CMakeLists.txt",
    "content": "add_custom_command(OUTPUT echo.pb.cc echo.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/echo.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS echo.proto)\n\nset_source_files_properties(echo.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion -Wno-shadow\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(echo_proto echo.pb.cc)\ntarget_link_libraries(echo_proto protobuf pthread)\n\nadd_executable(protobuf_rpc_echo_client client.cc)\nset_target_properties(protobuf_rpc_echo_client PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_echo_client echo_proto muduo_protorpc)\n\nadd_executable(protobuf_rpc_echo_server server.cc)\nset_target_properties(protobuf_rpc_echo_server PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(protobuf_rpc_echo_server echo_proto muduo_protorpc)\n"
  },
  {
    "path": "examples/protobuf/rpcbench/client.cc",
    "content": "#include \"examples/protobuf/rpcbench/echo.pb.h\"\n\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpConnection.h\"\n#include \"muduo/net/protorpc/RpcChannel.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstatic const int kRequests = 50000;\n\nclass RpcClient : noncopyable\n{\n public:\n\n  RpcClient(EventLoop* loop,\n            const InetAddress& serverAddr,\n            CountDownLatch* allConnected,\n            CountDownLatch* allFinished)\n    : // loop_(loop),\n      client_(loop, serverAddr, \"RpcClient\"),\n      channel_(new RpcChannel),\n      stub_(get_pointer(channel_)),\n      allConnected_(allConnected),\n      allFinished_(allFinished),\n      count_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&RpcClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&RpcChannel::onMessage, get_pointer(channel_), _1, _2, _3));\n    // client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void sendRequest()\n  {\n    echo::EchoRequest request;\n    request.set_payload(\"001010\");\n    echo::EchoResponse* response = new echo::EchoResponse;\n    stub_.Echo(NULL, &request, response, NewCallback(this, &RpcClient::replied, response));\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      //channel_.reset(new RpcChannel(conn));\n      conn->setTcpNoDelay(true);\n      channel_->setConnection(conn);\n      allConnected_->countDown();\n    }\n  }\n\n  void replied(echo::EchoResponse* resp)\n  {\n    // LOG_INFO << \"replied:\\n\" << resp->DebugString();\n    // loop_->quit();\n    ++count_;\n    if (count_ < kRequests)\n    {\n      sendRequest();\n    }\n    else\n    {\n      LOG_INFO << \"RpcClient \" << this << \" finished\";\n      allFinished_->countDown();\n    }\n  }\n\n  // EventLoop* loop_;\n  TcpClient client_;\n  RpcChannelPtr channel_;\n  echo::EchoService::Stub stub_;\n  CountDownLatch* allConnected_;\n  CountDownLatch* allFinished_;\n  int count_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    int nClients = 1;\n\n    if (argc > 2)\n    {\n      nClients = atoi(argv[2]);\n    }\n\n    int nThreads = 1;\n\n    if (argc > 3)\n    {\n      nThreads = atoi(argv[3]);\n    }\n\n    CountDownLatch allConnected(nClients);\n    CountDownLatch allFinished(nClients);\n\n    EventLoop loop;\n    EventLoopThreadPool pool(&loop, \"rpcbench-client\");\n    pool.setThreadNum(nThreads);\n    pool.start();\n    InetAddress serverAddr(argv[1], 8888);\n\n    std::vector<std::unique_ptr<RpcClient>> clients;\n    for (int i = 0; i < nClients; ++i)\n    {\n      clients.emplace_back(new RpcClient(pool.getNextLoop(), serverAddr, &allConnected, &allFinished));\n      clients.back()->connect();\n    }\n    allConnected.wait();\n    Timestamp start(Timestamp::now());\n    LOG_INFO << \"all connected\";\n    for (int i = 0; i < nClients; ++i)\n    {\n      clients[i]->sendRequest();\n    }\n    allFinished.wait();\n    Timestamp end(Timestamp::now());\n    LOG_INFO << \"all finished\";\n    double seconds = timeDifference(end, start);\n    printf(\"%f seconds\\n\", seconds);\n    printf(\"%.1f calls per second\\n\", nClients * kRequests / seconds);\n\n    exit(0);\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip numClients [numThreads]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpcbench/echo.proto",
    "content": "package echo;\n//option py_generic_services = true;\noption cc_generic_services = true;\noption java_generic_services = true;\noption java_package = \"echo\";\noption java_outer_classname = \"EchoProto\";\n\nmessage EchoRequest {\n  required string payload = 1;\n}\n\nmessage EchoResponse {\n  required string payload = 2;\n}\n\nservice EchoService {\n  rpc Echo (EchoRequest) returns (EchoResponse);\n}\n\n"
  },
  {
    "path": "examples/protobuf/rpcbench/server.cc",
    "content": "#include \"examples/protobuf/rpcbench/echo.pb.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/protorpc/RpcServer.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace echo\n{\n\nclass EchoServiceImpl : public EchoService\n{\n public:\n  virtual void Echo(::google::protobuf::RpcController* controller,\n                    const ::echo::EchoRequest* request,\n                    ::echo::EchoResponse* response,\n                    ::google::protobuf::Closure* done)\n  {\n    //LOG_INFO << \"EchoServiceImpl::Solve\";\n    response->set_payload(request->payload());\n    done->Run();\n  }\n};\n\n}  // namespace echo\n\nint main(int argc, char* argv[])\n{\n  int nThreads =  argc > 1 ? atoi(argv[1]) : 1;\n  LOG_INFO << \"pid = \" << getpid() << \" threads = \" << nThreads;\n  EventLoop loop;\n  int port = argc > 2 ? atoi(argv[2]) : 8888;\n  InetAddress listenAddr(static_cast<uint16_t>(port));\n  echo::EchoServiceImpl impl;\n  RpcServer server(&loop, listenAddr);\n  server.setThreadNum(nThreads);\n  server.registerService(&impl);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/roundtrip/CMakeLists.txt",
    "content": "add_executable(roundtrip roundtrip.cc)\ntarget_link_libraries(roundtrip muduo_net)\n\nadd_executable(roundtrip_udp roundtrip_udp.cc)\ntarget_link_libraries(roundtrip_udp muduo_net)\n\n"
  },
  {
    "path": "examples/roundtrip/roundtrip.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst size_t frameLen = 2*sizeof(int64_t);\n\nvoid serverConnectionCallback(const TcpConnectionPtr& conn)\n{\n  LOG_TRACE << conn->name() << \" \" << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n  }\n  else\n  {\n  }\n}\n\nvoid serverMessageCallback(const TcpConnectionPtr& conn,\n                           Buffer* buffer,\n                           muduo::Timestamp receiveTime)\n{\n  int64_t message[2];\n  while (buffer->readableBytes() >= frameLen)\n  {\n    memcpy(message, buffer->peek(), frameLen);\n    buffer->retrieve(frameLen);\n    message[1] = receiveTime.microSecondsSinceEpoch();\n    conn->send(message, sizeof message);\n  }\n}\n\nvoid runServer(uint16_t port)\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(port), \"ClockServer\");\n  server.setConnectionCallback(serverConnectionCallback);\n  server.setMessageCallback(serverMessageCallback);\n  server.start();\n  loop.loop();\n}\n\nTcpConnectionPtr clientConnection;\n\nvoid clientConnectionCallback(const TcpConnectionPtr& conn)\n{\n  LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    clientConnection = conn;\n    conn->setTcpNoDelay(true);\n  }\n  else\n  {\n    clientConnection.reset();\n  }\n}\n\nvoid clientMessageCallback(const TcpConnectionPtr&,\n                           Buffer* buffer,\n                           muduo::Timestamp receiveTime)\n{\n  int64_t message[2];\n  while (buffer->readableBytes() >= frameLen)\n  {\n    memcpy(message, buffer->peek(), frameLen);\n    buffer->retrieve(frameLen);\n    int64_t send = message[0];\n    int64_t their = message[1];\n    int64_t back = receiveTime.microSecondsSinceEpoch();\n    int64_t mine = (back+send)/2;\n    LOG_INFO << \"round trip \" << back - send\n             << \" clock error \" << their - mine;\n  }\n}\n\nvoid sendMyTime()\n{\n  if (clientConnection)\n  {\n    int64_t message[2] = { 0, 0 };\n    message[0] = Timestamp::now().microSecondsSinceEpoch();\n    clientConnection->send(message, sizeof message);\n  }\n}\n\nvoid runClient(const char* ip, uint16_t port)\n{\n  EventLoop loop;\n  TcpClient client(&loop, InetAddress(ip, port), \"ClockClient\");\n  client.enableRetry();\n  client.setConnectionCallback(clientConnectionCallback);\n  client.setMessageCallback(clientMessageCallback);\n  client.connect();\n  loop.runEvery(0.2, sendMyTime);\n  loop.loop();\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc > 2)\n  {\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    if (strcmp(argv[1], \"-s\") == 0)\n    {\n      runServer(port);\n    }\n    else\n    {\n      runClient(argv[1], port);\n    }\n  }\n  else\n  {\n    printf(\"Usage:\\n%s -s port\\n%s ip port\\n\", argv[0], argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/roundtrip/roundtrip_udp.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/Socket.h\"\n#include \"muduo/net/SocketsOps.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst size_t frameLen = 2*sizeof(int64_t);\n\nint createNonblockingUDP()\n{\n  int sockfd = ::socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_UDP);\n  if (sockfd < 0)\n  {\n    LOG_SYSFATAL << \"::socket\";\n  }\n  return sockfd;\n}\n\n/////////////////////////////// Server ///////////////////////////////\n\nvoid serverReadCallback(int sockfd, muduo::Timestamp receiveTime)\n{\n  int64_t message[2];\n  struct sockaddr peerAddr;\n  memZero(&peerAddr, sizeof peerAddr);\n  socklen_t addrLen = sizeof peerAddr;\n  ssize_t nr = ::recvfrom(sockfd, message, sizeof message, 0, &peerAddr, &addrLen);\n\n  char addrStr[64];\n  sockets::toIpPort(addrStr, sizeof addrStr, &peerAddr);\n  LOG_DEBUG << \"received \" << nr << \" bytes from \" << addrStr;\n\n  if (nr < 0)\n  {\n    LOG_SYSERR << \"::recvfrom\";\n  }\n  else if (implicit_cast<size_t>(nr) == frameLen)\n  {\n    message[1] = receiveTime.microSecondsSinceEpoch();\n    ssize_t nw = ::sendto(sockfd, message, sizeof message, 0, &peerAddr, addrLen);\n    if (nw < 0)\n    {\n      LOG_SYSERR << \"::sendto\";\n    }\n    else if (implicit_cast<size_t>(nw) != frameLen)\n    {\n      LOG_ERROR << \"Expect \" << frameLen << \" bytes, wrote \" << nw << \" bytes.\";\n    }\n  }\n  else\n  {\n    LOG_ERROR << \"Expect \" << frameLen << \" bytes, received \" << nr << \" bytes.\";\n  }\n}\n\nvoid runServer(uint16_t port)\n{\n  Socket sock(createNonblockingUDP());\n  sock.bindAddress(InetAddress(port));\n  EventLoop loop;\n  Channel channel(&loop, sock.fd());\n  channel.setReadCallback(std::bind(&serverReadCallback, sock.fd(), _1));\n  channel.enableReading();\n  loop.loop();\n}\n\n/////////////////////////////// Client ///////////////////////////////\n\nvoid clientReadCallback(int sockfd, muduo::Timestamp receiveTime)\n{\n  int64_t message[2];\n  ssize_t nr = sockets::read(sockfd, message, sizeof message);\n\n  if (nr < 0)\n  {\n    LOG_SYSERR << \"::read\";\n  }\n  else if (implicit_cast<size_t>(nr) == frameLen)\n  {\n    int64_t send = message[0];\n    int64_t their = message[1];\n    int64_t back = receiveTime.microSecondsSinceEpoch();\n    int64_t mine = (back+send)/2;\n    LOG_INFO << \"round trip \" << back - send\n             << \" clock error \" << their - mine;\n  }\n  else\n  {\n    LOG_ERROR << \"Expect \" << frameLen << \" bytes, received \" << nr << \" bytes.\";\n  }\n}\n\nvoid sendMyTime(int sockfd)\n{\n  int64_t message[2] = { 0, 0 };\n  message[0] = Timestamp::now().microSecondsSinceEpoch();\n  ssize_t nw = sockets::write(sockfd, message, sizeof message);\n  if (nw < 0)\n  {\n    LOG_SYSERR << \"::write\";\n  }\n  else if (implicit_cast<size_t>(nw) != frameLen)\n  {\n    LOG_ERROR << \"Expect \" << frameLen << \" bytes, wrote \" << nw << \" bytes.\";\n  }\n}\n\nvoid runClient(const char* ip, uint16_t port)\n{\n  Socket sock(createNonblockingUDP());\n  InetAddress serverAddr(ip, port);\n  int ret = sockets::connect(sock.fd(), serverAddr.getSockAddr());\n  if (ret < 0)\n  {\n    LOG_SYSFATAL << \"::connect\";\n  }\n  EventLoop loop;\n  Channel channel(&loop, sock.fd());\n  channel.setReadCallback(std::bind(&clientReadCallback, sock.fd(), _1));\n  channel.enableReading();\n  loop.runEvery(0.2, std::bind(sendMyTime, sock.fd()));\n  loop.loop();\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc > 2)\n  {\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    if (strcmp(argv[1], \"-s\") == 0)\n    {\n      runServer(port);\n    }\n    else\n    {\n      runClient(argv[1], port);\n    }\n  }\n  else\n  {\n    printf(\"Usage:\\n%s -s port\\n%s ip port\\n\", argv[0], argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/shorturl/CMakeLists.txt",
    "content": "add_executable(shorturl shorturl.cc)\ntarget_link_libraries(shorturl muduo_http)\n\n"
  },
  {
    "path": "examples/shorturl/shorturl.cc",
    "content": "#include \"muduo/net/http/HttpServer.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpResponse.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <map>\n\n#include <sys/socket.h>  // SO_REUSEPORT\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nextern char favicon[555];\nbool benchmark = false;\n\nstd::map<string, string> redirections;\n\nvoid onRequest(const HttpRequest& req, HttpResponse* resp)\n{\n  LOG_INFO << \"Headers \" << req.methodString() << \" \" << req.path();\n  if (!benchmark)\n  {\n    const std::map<string, string>& headers = req.headers();\n    for (std::map<string, string>::const_iterator it = headers.begin();\n        it != headers.end();\n        ++it)\n    {\n      LOG_DEBUG << it->first << \": \" << it->second;\n    }\n  }\n\n  // TODO: support PUT and DELETE to create new redirections on-the-fly.\n\n  std::map<string, string>::const_iterator it = redirections.find(req.path());\n  if (it != redirections.end())\n  {\n    resp->setStatusCode(HttpResponse::k301MovedPermanently);\n    resp->setStatusMessage(\"Moved Permanently\");\n    resp->addHeader(\"Location\", it->second);\n    // resp->setCloseConnection(true);\n  }\n  else if (req.path() == \"/\")\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"text/html\");\n    string now = Timestamp::now().toFormattedString();\n    std::map<string, string>::const_iterator i = redirections.begin();\n    string text;\n    for (; i != redirections.end(); ++i)\n    {\n      text.append(\"<ul>\" + i->first + \" =&gt; \" + i->second + \"</ul>\");\n    }\n\n    resp->setBody(\"<html><head><title>My tiny short url service</title></head>\"\n        \"<body><h1>Known redirections</h1>\"\n        + text +\n        \"Now is \" + now +\n        \"</body></html>\");\n  }\n  else if (req.path() == \"/favicon.ico\")\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"image/png\");\n    resp->setBody(string(favicon, sizeof favicon));\n  }\n  else\n  {\n    resp->setStatusCode(HttpResponse::k404NotFound);\n    resp->setStatusMessage(\"Not Found\");\n    resp->setCloseConnection(true);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  redirections[\"/1\"] = \"http://chenshuo.com\";\n  redirections[\"/2\"] = \"http://blog.csdn.net/Solstice\";\n\n  int numThreads = 0;\n  if (argc > 1)\n  {\n    benchmark = true;\n    Logger::setLogLevel(Logger::WARN);\n    numThreads = atoi(argv[1]);\n  }\n\n#ifdef SO_REUSEPORT\n  LOG_WARN << \"SO_REUSEPORT\";\n  EventLoop loop;\n  EventLoopThreadPool threadPool(&loop, \"shorturl\");\n  if (numThreads > 1)\n  {\n    threadPool.setThreadNum(numThreads);\n  }\n  else\n  {\n    numThreads = 1;\n  }\n  threadPool.start();\n\n  std::vector<std::unique_ptr<HttpServer>> servers;\n  for (int i = 0; i < numThreads; ++i)\n  {\n    servers.emplace_back(new HttpServer(threadPool.getNextLoop(),\n                                        InetAddress(8000),\n                                        \"shorturl\",\n                                        TcpServer::kReusePort));\n    servers.back()->setHttpCallback(onRequest);\n    servers.back()->getLoop()->runInLoop(\n        std::bind(&HttpServer::start, servers.back().get()));\n  }\n  loop.loop();\n#else\n  LOG_WARN << \"Normal\";\n  EventLoop loop;\n  HttpServer server(&loop, InetAddress(8000), \"shorturl\");\n  server.setHttpCallback(onRequest);\n  server.setThreadNum(numThreads);\n  server.start();\n  loop.loop();\n#endif\n}\n\nchar favicon[555] = {\n  '\\x89', 'P', 'N', 'G', '\\xD', '\\xA', '\\x1A', '\\xA',\n  '\\x0', '\\x0', '\\x0', '\\xD', 'I', 'H', 'D', 'R',\n  '\\x0', '\\x0', '\\x0', '\\x10', '\\x0', '\\x0', '\\x0', '\\x10',\n  '\\x8', '\\x6', '\\x0', '\\x0', '\\x0', '\\x1F', '\\xF3', '\\xFF',\n  'a', '\\x0', '\\x0', '\\x0', '\\x19', 't', 'E', 'X',\n  't', 'S', 'o', 'f', 't', 'w', 'a', 'r',\n  'e', '\\x0', 'A', 'd', 'o', 'b', 'e', '\\x20',\n  'I', 'm', 'a', 'g', 'e', 'R', 'e', 'a',\n  'd', 'y', 'q', '\\xC9', 'e', '\\x3C', '\\x0', '\\x0',\n  '\\x1', '\\xCD', 'I', 'D', 'A', 'T', 'x', '\\xDA',\n  '\\x94', '\\x93', '9', 'H', '\\x3', 'A', '\\x14', '\\x86',\n  '\\xFF', '\\x5D', 'b', '\\xA7', '\\x4', 'R', '\\xC4', 'm',\n  '\\x22', '\\x1E', '\\xA0', 'F', '\\x24', '\\x8', '\\x16', '\\x16',\n  'v', '\\xA', '6', '\\xBA', 'J', '\\x9A', '\\x80', '\\x8',\n  'A', '\\xB4', 'q', '\\x85', 'X', '\\x89', 'G', '\\xB0',\n  'I', '\\xA9', 'Q', '\\x24', '\\xCD', '\\xA6', '\\x8', '\\xA4',\n  'H', 'c', '\\x91', 'B', '\\xB', '\\xAF', 'V', '\\xC1',\n  'F', '\\xB4', '\\x15', '\\xCF', '\\x22', 'X', '\\x98', '\\xB',\n  'T', 'H', '\\x8A', 'd', '\\x93', '\\x8D', '\\xFB', 'F',\n  'g', '\\xC9', '\\x1A', '\\x14', '\\x7D', '\\xF0', 'f', 'v',\n  'f', '\\xDF', '\\x7C', '\\xEF', '\\xE7', 'g', 'F', '\\xA8',\n  '\\xD5', 'j', 'H', '\\x24', '\\x12', '\\x2A', '\\x0', '\\x5',\n  '\\xBF', 'G', '\\xD4', '\\xEF', '\\xF7', '\\x2F', '6', '\\xEC',\n  '\\x12', '\\x20', '\\x1E', '\\x8F', '\\xD7', '\\xAA', '\\xD5', '\\xEA',\n  '\\xAF', 'I', '5', 'F', '\\xAA', 'T', '\\x5F', '\\x9F',\n  '\\x22', 'A', '\\x2A', '\\x95', '\\xA', '\\x83', '\\xE5', 'r',\n  '9', 'd', '\\xB3', 'Y', '\\x96', '\\x99', 'L', '\\x6',\n  '\\xE9', 't', '\\x9A', '\\x25', '\\x85', '\\x2C', '\\xCB', 'T',\n  '\\xA7', '\\xC4', 'b', '1', '\\xB5', '\\x5E', '\\x0', '\\x3',\n  'h', '\\x9A', '\\xC6', '\\x16', '\\x82', '\\x20', 'X', 'R',\n  '\\x14', 'E', '6', 'S', '\\x94', '\\xCB', 'e', 'x',\n  '\\xBD', '\\x5E', '\\xAA', 'U', 'T', '\\x23', 'L', '\\xC0',\n  '\\xE0', '\\xE2', '\\xC1', '\\x8F', '\\x0', '\\x9E', '\\xBC', '\\x9',\n  'A', '\\x7C', '\\x3E', '\\x1F', '\\x83', 'D', '\\x22', '\\x11',\n  '\\xD5', 'T', '\\x40', '\\x3F', '8', '\\x80', 'w', '\\xE5',\n  '3', '\\x7', '\\xB8', '\\x5C', '\\x2E', 'H', '\\x92', '\\x4',\n  '\\x87', '\\xC3', '\\x81', '\\x40', '\\x20', '\\x40', 'g', '\\x98',\n  '\\xE9', '6', '\\x1A', '\\xA6', 'g', '\\x15', '\\x4', '\\xE3',\n  '\\xD7', '\\xC8', '\\xBD', '\\x15', '\\xE1', 'i', '\\xB7', 'C',\n  '\\xAB', '\\xEA', 'x', '\\x2F', 'j', 'X', '\\x92', '\\xBB',\n  '\\x18', '\\x20', '\\x9F', '\\xCF', '3', '\\xC3', '\\xB8', '\\xE9',\n  'N', '\\xA7', '\\xD3', 'l', 'J', '\\x0', 'i', '6',\n  '\\x7C', '\\x8E', '\\xE1', '\\xFE', 'V', '\\x84', '\\xE7', '\\x3C',\n  '\\x9F', 'r', '\\x2B', '\\x3A', 'B', '\\x7B', '7', 'f',\n  'w', '\\xAE', '\\x8E', '\\xE', '\\xF3', '\\xBD', 'R', '\\xA9',\n  'd', '\\x2', 'B', '\\xAF', '\\x85', '2', 'f', 'F',\n  '\\xBA', '\\xC', '\\xD9', '\\x9F', '\\x1D', '\\x9A', 'l', '\\x22',\n  '\\xE6', '\\xC7', '\\x3A', '\\x2C', '\\x80', '\\xEF', '\\xC1', '\\x15',\n  '\\x90', '\\x7', '\\x93', '\\xA2', '\\x28', '\\xA0', 'S', 'j',\n  '\\xB1', '\\xB8', '\\xDF', '\\x29', '5', 'C', '\\xE', '\\x3F',\n  'X', '\\xFC', '\\x98', '\\xDA', 'y', 'j', 'P', '\\x40',\n  '\\x0', '\\x87', '\\xAE', '\\x1B', '\\x17', 'B', '\\xB4', '\\x3A',\n  '\\x3F', '\\xBE', 'y', '\\xC7', '\\xA', '\\x26', '\\xB6', '\\xEE',\n  '\\xD9', '\\x9A', '\\x60', '\\x14', '\\x93', '\\xDB', '\\x8F', '\\xD',\n  '\\xA', '\\x2E', '\\xE9', '\\x23', '\\x95', '\\x29', 'X', '\\x0',\n  '\\x27', '\\xEB', 'n', 'V', 'p', '\\xBC', '\\xD6', '\\xCB',\n  '\\xD6', 'G', '\\xAB', '\\x3D', 'l', '\\x7D', '\\xB8', '\\xD2',\n  '\\xDD', '\\xA0', '\\x60', '\\x83', '\\xBA', '\\xEF', '\\x5F', '\\xA4',\n  '\\xEA', '\\xCC', '\\x2', 'N', '\\xAE', '\\x5E', 'p', '\\x1A',\n  '\\xEC', '\\xB3', '\\x40', '9', '\\xAC', '\\xFE', '\\xF2', '\\x91',\n  '\\x89', 'g', '\\x91', '\\x85', '\\x21', '\\xA8', '\\x87', '\\xB7',\n  'X', '\\x7E', '\\x7E', '\\x85', '\\xBB', '\\xCD', 'N', 'N',\n  'b', 't', '\\x40', '\\xFA', '\\x93', '\\x89', '\\xEC', '\\x1E',\n  '\\xEC', '\\x86', '\\x2', 'H', '\\x26', '\\x93', '\\xD0', 'u',\n  '\\x1D', '\\x7F', '\\x9', '2', '\\x95', '\\xBF', '\\x1F', '\\xDB',\n  '\\xD7', 'c', '\\x8A', '\\x1A', '\\xF7', '\\x5C', '\\xC1', '\\xFF',\n  '\\x22', 'J', '\\xC3', '\\x87', '\\x0', '\\x3', '\\x0', 'K',\n  '\\xBB', '\\xF8', '\\xD6', '\\x2A', 'v', '\\x98', 'I', '\\x0',\n  '\\x0', '\\x0', '\\x0', 'I', 'E', 'N', 'D', '\\xAE',\n  'B', '\\x60', '\\x82',\n};\n"
  },
  {
    "path": "examples/simple/CMakeLists.txt",
    "content": "add_executable(simple_chargen chargen/chargen.cc chargen/main.cc)\ntarget_link_libraries(simple_chargen muduo_net)\n\nadd_executable(simple_daytime daytime/daytime.cc daytime/main.cc)\ntarget_link_libraries(simple_daytime muduo_net)\n\nadd_executable(simple_discard discard/discard.cc discard/main.cc)\ntarget_link_libraries(simple_discard muduo_net)\n\nadd_executable(simple_echo echo/echo.cc echo/main.cc)\ntarget_link_libraries(simple_echo muduo_net)\n\nadd_executable(simple_time time/time.cc time/main.cc)\ntarget_link_libraries(simple_time muduo_net)\n\nadd_executable(simple_allinone\n  allinone/allinone.cc\n  chargen/chargen.cc\n  daytime/daytime.cc\n  discard/discard.cc\n  echo/echo.cc\n  time/time.cc\n  )\ntarget_link_libraries(simple_allinone muduo_net)\n\nadd_executable(simple_timeclient timeclient/timeclient.cc)\ntarget_link_libraries(simple_timeclient muduo_net)\n\nadd_executable(simple_chargenclient chargenclient/chargenclient.cc)\ntarget_link_libraries(simple_chargenclient muduo_net)\n\n"
  },
  {
    "path": "examples/simple/allinone/allinone.cc",
    "content": "#include \"examples/simple/chargen/chargen.h\"\n#include \"examples/simple/daytime/daytime.h\"\n#include \"examples/simple/discard/discard.h\"\n#include \"examples/simple/echo/echo.h\"\n#include \"examples/simple/time/time.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;  // one loop shared by multiple servers\n\n  ChargenServer chargenServer(&loop, InetAddress(2019));\n  chargenServer.start();\n\n  DaytimeServer daytimeServer(&loop, InetAddress(2013));\n  daytimeServer.start();\n\n  DiscardServer discardServer(&loop, InetAddress(2009));\n  discardServer.start();\n\n  EchoServer echoServer(&loop, InetAddress(2007));\n  echoServer.start();\n\n  TimeServer timeServer(&loop, InetAddress(2037));\n  timeServer.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/chargen/chargen.cc",
    "content": "#include \"examples/simple/chargen/chargen.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nChargenServer::ChargenServer(EventLoop* loop,\n                             const InetAddress& listenAddr,\n                             bool print)\n  : server_(loop, listenAddr, \"ChargenServer\"),\n    transferred_(0),\n    startTime_(Timestamp::now())\n{\n  server_.setConnectionCallback(\n      std::bind(&ChargenServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&ChargenServer::onMessage, this, _1, _2, _3));\n  server_.setWriteCompleteCallback(\n      std::bind(&ChargenServer::onWriteComplete, this, _1));\n  if (print)\n  {\n    loop->runEvery(3.0, std::bind(&ChargenServer::printThroughput, this));\n  }\n\n  string line;\n  for (int i = 33; i < 127; ++i)\n  {\n    line.push_back(char(i));\n  }\n  line += line;\n\n  for (size_t i = 0; i < 127-33; ++i)\n  {\n    message_ += line.substr(i, 72) + '\\n';\n  }\n}\n\nvoid ChargenServer::start()\n{\n  server_.start();\n}\n\nvoid ChargenServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"ChargenServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n    conn->send(message_);\n  }\n}\n\nvoid ChargenServer::onMessage(const TcpConnectionPtr& conn,\n                              Buffer* buf,\n                              Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" discards \" << msg.size()\n           << \" bytes received at \" << time.toString();\n}\n\nvoid ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)\n{\n  transferred_ += message_.size();\n  conn->send(message_);\n}\n\nvoid ChargenServer::printThroughput()\n{\n  Timestamp endTime = Timestamp::now();\n  double time = timeDifference(endTime, startTime_);\n  printf(\"%4.3f MiB/s\\n\", static_cast<double>(transferred_)/time/1024/1024);\n  transferred_ = 0;\n  startTime_ = endTime;\n}\n\n"
  },
  {
    "path": "examples/simple/chargen/chargen.h",
    "content": "#ifndef MUDUO_EXAMPLES_SIMPLE_CHARGEN_CHARGEN_H\n#define MUDUO_EXAMPLES_SIMPLE_CHARGEN_CHARGEN_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 864\nclass ChargenServer\n{\n public:\n  ChargenServer(muduo::net::EventLoop* loop,\n                const muduo::net::InetAddress& listenAddr,\n                bool print = false);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  void onWriteComplete(const muduo::net::TcpConnectionPtr& conn);\n  void printThroughput();\n\n  muduo::net::TcpServer server_;\n\n  muduo::string message_;\n  int64_t transferred_;\n  muduo::Timestamp startTime_;\n};\n\n#endif  // MUDUO_EXAMPLES_SIMPLE_CHARGEN_CHARGEN_H\n"
  },
  {
    "path": "examples/simple/chargen/main.cc",
    "content": "#include \"examples/simple/chargen/chargen.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2019);\n  ChargenServer server(&loop, listenAddr, true);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/chargenclient/chargenclient.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass ChargenClient : noncopyable\n{\n public:\n  ChargenClient(EventLoop* loop, const InetAddress& listenAddr)\n    : loop_(loop),\n      client_(loop, listenAddr, \"ChargenClient\")\n  {\n    client_.setConnectionCallback(\n        std::bind(&ChargenClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&ChargenClient::onMessage, this, _1, _2, _3));\n    // client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (!conn->connected())\n      loop_->quit();\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)\n  {\n    buf->retrieveAll();\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 2019);\n\n    ChargenClient chargenClient(&loop, serverAddr);\n    chargenClient.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/simple/daytime/daytime.cc",
    "content": "#include \"examples/simple/daytime/daytime.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nDaytimeServer::DaytimeServer(EventLoop* loop,\n                             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"DaytimeServer\")\n{\n  server_.setConnectionCallback(\n      std::bind(&DaytimeServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&DaytimeServer::onMessage, this, _1, _2, _3));\n}\n\nvoid DaytimeServer::start()\n{\n  server_.start();\n}\n\nvoid DaytimeServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"DaytimeServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    conn->send(Timestamp::now().toFormattedString() + \"\\n\");\n    conn->shutdown();\n  }\n}\n\nvoid DaytimeServer::onMessage(const TcpConnectionPtr& conn,\n                              Buffer* buf,\n                              Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" discards \" << msg.size()\n           << \" bytes received at \" << time.toString();\n}\n\n"
  },
  {
    "path": "examples/simple/daytime/daytime.h",
    "content": "#ifndef MUDUO_EXAMPLES_SIMPLE_DAYTIME_DAYTIME_H\n#define MUDUO_EXAMPLES_SIMPLE_DAYTIME_DAYTIME_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 867\nclass DaytimeServer\n{\n public:\n  DaytimeServer(muduo::net::EventLoop* loop,\n                const muduo::net::InetAddress& listenAddr);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  muduo::net::TcpServer server_;\n};\n\n#endif  // MUDUO_EXAMPLES_SIMPLE_DAYTIME_DAYTIME_H\n"
  },
  {
    "path": "examples/simple/daytime/main.cc",
    "content": "#include \"examples/simple/daytime/daytime.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2013);\n  DaytimeServer server(&loop, listenAddr);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/discard/discard.cc",
    "content": "#include \"examples/simple/discard/discard.h\"\n\n#include \"muduo/base/Logging.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nDiscardServer::DiscardServer(EventLoop* loop,\n                             const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"DiscardServer\")\n{\n  server_.setConnectionCallback(\n      std::bind(&DiscardServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&DiscardServer::onMessage, this, _1, _2, _3));\n}\n\nvoid DiscardServer::start()\n{\n  server_.start();\n}\n\nvoid DiscardServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"DiscardServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n}\n\nvoid DiscardServer::onMessage(const TcpConnectionPtr& conn,\n                              Buffer* buf,\n                              Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" discards \" << msg.size()\n           << \" bytes received at \" << time.toString();\n}\n\n"
  },
  {
    "path": "examples/simple/discard/discard.h",
    "content": "#ifndef MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H\n#define MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 863\nclass DiscardServer\n{\n public:\n  DiscardServer(muduo::net::EventLoop* loop,\n                const muduo::net::InetAddress& listenAddr);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  muduo::net::TcpServer server_;\n};\n\n#endif  // MUDUO_EXAMPLES_SIMPLE_DISCARD_DISCARD_H\n"
  },
  {
    "path": "examples/simple/discard/main.cc",
    "content": "#include \"examples/simple/discard/discard.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2009);\n  DiscardServer server(&loop, listenAddr);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/echo/echo.cc",
    "content": "#include \"examples/simple/echo/echo.h\"\n\n#include \"muduo/base/Logging.h\"\n\nusing std::placeholders::_1;\nusing std::placeholders::_2;\nusing std::placeholders::_3;\n\n// using namespace muduo;\n// using namespace muduo::net;\n\nEchoServer::EchoServer(muduo::net::EventLoop* loop,\n                       const muduo::net::InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"EchoServer\")\n{\n  server_.setConnectionCallback(\n      std::bind(&EchoServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n}\n\nvoid EchoServer::start()\n{\n  server_.start();\n}\n\nvoid EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"EchoServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n}\n\nvoid EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,\n                           muduo::net::Buffer* buf,\n                           muduo::Timestamp time)\n{\n  muduo::string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" echo \" << msg.size() << \" bytes, \"\n           << \"data received at \" << time.toString();\n  conn->send(msg);\n}\n\n"
  },
  {
    "path": "examples/simple/echo/echo.h",
    "content": "#ifndef MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H\n#define MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 862\nclass EchoServer\n{\n public:\n  EchoServer(muduo::net::EventLoop* loop,\n             const muduo::net::InetAddress& listenAddr);\n\n  void start();  // calls server_.start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  muduo::net::TcpServer server_;\n};\n\n#endif  // MUDUO_EXAMPLES_SIMPLE_ECHO_ECHO_H\n"
  },
  {
    "path": "examples/simple/echo/main.cc",
    "content": "#include \"examples/simple/echo/echo.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\n// using namespace muduo;\n// using namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  muduo::net::EventLoop loop;\n  muduo::net::InetAddress listenAddr(2007);\n  EchoServer server(&loop, listenAddr);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/time/main.cc",
    "content": "#include \"examples/simple/time/time.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  LOG_INFO << \"pid = \" << getpid();\n  EventLoop loop;\n  InetAddress listenAddr(2037);\n  TimeServer server(&loop, listenAddr);\n  server.start();\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/simple/time/time.cc",
    "content": "#include \"examples/simple/time/time.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nTimeServer::TimeServer(muduo::net::EventLoop* loop,\n                             const muduo::net::InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"TimeServer\")\n{\n  server_.setConnectionCallback(\n      std::bind(&TimeServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&TimeServer::onMessage, this, _1, _2, _3));\n}\n\nvoid TimeServer::start()\n{\n  server_.start();\n}\n\nvoid TimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"TimeServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n           << conn->localAddress().toIpPort() << \" is \"\n           << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    time_t now = ::time(NULL);\n    int32_t be32 = sockets::hostToNetwork32(static_cast<int32_t>(now));\n    conn->send(&be32, sizeof be32);\n    conn->shutdown();\n  }\n}\n\nvoid TimeServer::onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time)\n{\n  string msg(buf->retrieveAllAsString());\n  LOG_INFO << conn->name() << \" discards \" << msg.size()\n           << \" bytes received at \" << time.toString();\n}\n\n"
  },
  {
    "path": "examples/simple/time/time.h",
    "content": "#ifndef MUDUO_EXAMPLES_SIMPLE_TIME_TIME_H\n#define MUDUO_EXAMPLES_SIMPLE_TIME_TIME_H\n\n#include \"muduo/net/TcpServer.h\"\n\n// RFC 868\nclass TimeServer\n{\n public:\n  TimeServer(muduo::net::EventLoop* loop,\n             const muduo::net::InetAddress& listenAddr);\n\n  void start();\n\n private:\n  void onConnection(const muduo::net::TcpConnectionPtr& conn);\n\n  void onMessage(const muduo::net::TcpConnectionPtr& conn,\n                 muduo::net::Buffer* buf,\n                 muduo::Timestamp time);\n\n  muduo::net::TcpServer server_;\n};\n\n#endif  // MUDUO_EXAMPLES_SIMPLE_TIME_TIME_H\n"
  },
  {
    "path": "examples/simple/timeclient/timeclient.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass TimeClient : noncopyable\n{\n public:\n  TimeClient(EventLoop* loop, const InetAddress& serverAddr)\n    : loop_(loop),\n      client_(loop, serverAddr, \"TimeClient\")\n  {\n    client_.setConnectionCallback(\n        std::bind(&TimeClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&TimeClient::onMessage, this, _1, _2, _3));\n    // client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n\n  EventLoop* loop_;\n  TcpClient client_;\n\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->localAddress().toIpPort() << \" -> \"\n             << conn->peerAddress().toIpPort() << \" is \"\n             << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (!conn->connected())\n    {\n      loop_->quit();\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)\n  {\n    if (buf->readableBytes() >= sizeof(int32_t))\n    {\n      const void* data = buf->peek();\n      int32_t be32 = *static_cast<const int32_t*>(data);\n      buf->retrieve(sizeof(int32_t));\n      time_t time = sockets::networkToHost32(be32);\n      Timestamp ts(implicit_cast<uint64_t>(time) * Timestamp::kMicroSecondsPerSecond);\n      LOG_INFO << \"Server time = \" << time << \", \" << ts.toFormattedString();\n    }\n    else\n    {\n      LOG_INFO << conn->name() << \" no enough data \" << buf->readableBytes()\n               << \" at \" << receiveTime.toFormattedString();\n    }\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    InetAddress serverAddr(argv[1], 2037);\n\n    TimeClient timeClient(&loop, serverAddr);\n    timeClient.connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "examples/socks4a/CMakeLists.txt",
    "content": "add_executable(balancer balancer.cc)\ntarget_link_libraries(balancer muduo_net)\n\nadd_executable(tcprelay tcprelay.cc)\ntarget_link_libraries(tcprelay muduo_net)\n\nadd_executable(socks4a socks4a.cc)\ntarget_link_libraries(socks4a muduo_net)\n\n"
  },
  {
    "path": "examples/socks4a/balancer.cc",
    "content": "#include \"examples/socks4a/tunnel.h\"\n\n#include \"muduo/base/ThreadLocal.h\"\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstd::vector<InetAddress> g_backends;\nThreadLocal<std::map<string, TunnelPtr> > t_tunnels;\nMutexLock g_mutex;\nsize_t g_current = 0;\n\nvoid onServerConnection(const TcpConnectionPtr& conn)\n{\n  LOG_DEBUG << (conn->connected() ? \"UP\" : \"DOWN\");\n  std::map<string, TunnelPtr>& tunnels = t_tunnels.value();\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n    conn->stopRead();\n    size_t current = 0;\n    {\n    MutexLockGuard guard(g_mutex);\n    current = g_current;\n    g_current = (g_current+1) % g_backends.size();\n    }\n\n    InetAddress backend = g_backends[current];\n    TunnelPtr tunnel(new Tunnel(conn->getLoop(), backend, conn));\n    tunnel->setup();\n    tunnel->connect();\n\n    tunnels[conn->name()] = tunnel;\n  }\n  else\n  {\n    assert(tunnels.find(conn->name()) != tunnels.end());\n    tunnels[conn->name()]->disconnect();\n    tunnels.erase(conn->name());\n  }\n}\n\nvoid onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n{\n  if (!conn->getContext().empty())\n  {\n    const TcpConnectionPtr& clientConn\n      = boost::any_cast<const TcpConnectionPtr&>(conn->getContext());\n    clientConn->send(buf);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 3)\n  {\n    fprintf(stderr, \"Usage: %s listen_port backend_ip:port [backend_ip:port]\\n\", argv[0]);\n  }\n  else\n  {\n    for (int i = 2; i < argc; ++i)\n    {\n      string hostport = argv[i];\n      size_t colon = hostport.find(':');\n      if (colon != string::npos)\n      {\n        string ip = hostport.substr(0, colon);\n        uint16_t port = static_cast<uint16_t>(atoi(hostport.c_str()+colon+1));\n        g_backends.push_back(InetAddress(ip, port));\n      }\n      else\n      {\n        fprintf(stderr, \"invalid backend address %s\\n\", argv[i]);\n        return 1;\n      }\n    }\n\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress listenAddr(port);\n\n    EventLoop loop;\n    TcpServer server(&loop, listenAddr, \"TcpBalancer\");\n    server.setConnectionCallback(onServerConnection);\n    server.setMessageCallback(onServerMessage);\n    server.setThreadNum(4);\n    server.start();\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/socks4a/socks4a.cc",
    "content": "#include \"examples/socks4a/tunnel.h\"\n\n#include \"muduo/net/Endian.h\"\n#include <stdio.h>\n#include <netdb.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoop* g_eventLoop;\nstd::map<string, TunnelPtr> g_tunnels;\n\nvoid onServerConnection(const TcpConnectionPtr& conn)\n{\n  LOG_DEBUG << conn->name() << (conn->connected() ? \" UP\" : \" DOWN\");\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n  }\n  else\n  {\n    std::map<string, TunnelPtr>::iterator it = g_tunnels.find(conn->name());\n    if (it != g_tunnels.end())\n    {\n      it->second->disconnect();\n      g_tunnels.erase(it);\n    }\n  }\n}\n\nvoid onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n{\n  LOG_DEBUG << conn->name() << \" \" << buf->readableBytes();\n  if (g_tunnels.find(conn->name()) == g_tunnels.end())\n  {\n    if (buf->readableBytes() > 128)\n    {\n      conn->shutdown();\n    }\n    else if (buf->readableBytes() > 8)\n    {\n      const char* begin = buf->peek() + 8;\n      const char* end = buf->peek() + buf->readableBytes();\n      const char* where = std::find(begin, end, '\\0');\n      if (where != end)\n      {\n        char ver = buf->peek()[0];\n        char cmd = buf->peek()[1];\n        const void* port = buf->peek() + 2;\n        const void* ip = buf->peek() + 4;\n\n        sockaddr_in addr;\n        memZero(&addr, sizeof addr);\n        addr.sin_family = AF_INET;\n        addr.sin_port = *static_cast<const in_port_t*>(port);\n        addr.sin_addr.s_addr = *static_cast<const uint32_t*>(ip);\n\n        bool socks4a = sockets::networkToHost32(addr.sin_addr.s_addr) < 256;\n        bool okay = false;\n        if (socks4a)\n        {\n          const char* endOfHostName = std::find(where+1, end, '\\0');\n          if (endOfHostName != end)\n          {\n            string hostname = where+1;\n            where = endOfHostName;\n            LOG_INFO << \"Socks4a host name \" << hostname;\n            InetAddress tmp;\n            if (InetAddress::resolve(hostname, &tmp))\n            {\n              addr.sin_addr.s_addr = tmp.ipv4NetEndian();\n              okay = true;\n            }\n          }\n          else\n          {\n            return;\n          }\n        }\n        else\n        {\n          okay = true;\n        }\n\n        InetAddress serverAddr(addr);\n        if (ver == 4 && cmd == 1 && okay)\n        {\n          TunnelPtr tunnel(new Tunnel(g_eventLoop, serverAddr, conn));\n          tunnel->setup();\n          tunnel->connect();\n          g_tunnels[conn->name()] = tunnel;\n          buf->retrieveUntil(where+1);\n          char response[] = \"\\000\\x5aUVWXYZ\";\n          memcpy(response+2, &addr.sin_port, 2);\n          memcpy(response+4, &addr.sin_addr.s_addr, 4);\n          conn->send(response, 8);\n        }\n        else\n        {\n          char response[] = \"\\000\\x5bUVWXYZ\";\n          conn->send(response, 8);\n          conn->shutdown();\n        }\n      }\n    }\n  }\n  else if (!conn->getContext().empty())\n  {\n    const TcpConnectionPtr& clientConn\n      = boost::any_cast<const TcpConnectionPtr&>(conn->getContext());\n    clientConn->send(buf);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 2)\n  {\n    fprintf(stderr, \"Usage: %s <listen_port>\\n\", argv[0]);\n  }\n  else\n  {\n    LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    InetAddress listenAddr(port);\n\n    EventLoop loop;\n    g_eventLoop = &loop;\n\n    TcpServer server(&loop, listenAddr, \"Socks4\");\n\n    server.setConnectionCallback(onServerConnection);\n    server.setMessageCallback(onServerMessage);\n\n    server.start();\n\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/socks4a/tcprelay.cc",
    "content": "#include \"examples/socks4a/tunnel.h\"\n\n#include <malloc.h>\n#include <stdio.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoop* g_eventLoop;\nInetAddress* g_serverAddr;\nstd::map<string, TunnelPtr> g_tunnels;\n\nvoid onServerConnection(const TcpConnectionPtr& conn)\n{\n  LOG_DEBUG << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(true);\n    conn->stopRead();\n    TunnelPtr tunnel(new Tunnel(g_eventLoop, *g_serverAddr, conn));\n    tunnel->setup();\n    tunnel->connect();\n    g_tunnels[conn->name()] = tunnel;\n  }\n  else\n  {\n    assert(g_tunnels.find(conn->name()) != g_tunnels.end());\n    g_tunnels[conn->name()]->disconnect();\n    g_tunnels.erase(conn->name());\n  }\n}\n\nvoid onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n{\n  LOG_DEBUG << buf->readableBytes();\n  if (!conn->getContext().empty())\n  {\n    const TcpConnectionPtr& clientConn\n      = boost::any_cast<const TcpConnectionPtr&>(conn->getContext());\n    clientConn->send(buf);\n  }\n}\n\nvoid memstat()\n{\n  malloc_stats();\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 4)\n  {\n    fprintf(stderr, \"Usage: %s <host_ip> <port> <listen_port>\\n\", argv[0]);\n  }\n  else\n  {\n    LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n    {\n      // set max virtual memory to 256MB.\n      size_t kOneMB = 1024*1024;\n      rlimit rl = { 256*kOneMB, 256*kOneMB };\n      setrlimit(RLIMIT_AS, &rl);\n    }\n    const char* ip = argv[1];\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    InetAddress serverAddr(ip, port);\n    g_serverAddr = &serverAddr;\n\n    uint16_t acceptPort = static_cast<uint16_t>(atoi(argv[3]));\n    InetAddress listenAddr(acceptPort);\n\n    EventLoop loop;\n    g_eventLoop = &loop;\n    loop.runEvery(3, memstat);\n\n    TcpServer server(&loop, listenAddr, \"TcpRelay\");\n\n    server.setConnectionCallback(onServerConnection);\n    server.setMessageCallback(onServerMessage);\n\n    server.start();\n\n    loop.loop();\n  }\n}\n\n"
  },
  {
    "path": "examples/socks4a/tunnel.h",
    "content": "#ifndef MUDUO_EXAMPLES_SOCKS4A_TUNNEL_H\n#define MUDUO_EXAMPLES_SOCKS4A_TUNNEL_H\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"muduo/net/TcpServer.h\"\n\nclass Tunnel : public std::enable_shared_from_this<Tunnel>,\n               muduo::noncopyable\n{\n public:\n  Tunnel(muduo::net::EventLoop* loop,\n         const muduo::net::InetAddress& serverAddr,\n         const muduo::net::TcpConnectionPtr& serverConn)\n    : client_(loop, serverAddr, serverConn->name()),\n      serverConn_(serverConn)\n  {\n    LOG_INFO << \"Tunnel \" << serverConn->peerAddress().toIpPort()\n             << \" <-> \" << serverAddr.toIpPort();\n  }\n\n  ~Tunnel()\n  {\n    LOG_INFO << \"~Tunnel\";\n  }\n\n  void setup()\n  {\n    using std::placeholders::_1;\n    using std::placeholders::_2;\n    using std::placeholders::_3;\n\n    client_.setConnectionCallback(\n        std::bind(&Tunnel::onClientConnection, shared_from_this(), _1));\n    client_.setMessageCallback(\n        std::bind(&Tunnel::onClientMessage, shared_from_this(), _1, _2, _3));\n    serverConn_->setHighWaterMarkCallback(\n        std::bind(&Tunnel::onHighWaterMarkWeak,\n                  std::weak_ptr<Tunnel>(shared_from_this()), kServer, _1, _2),\n        1024*1024);\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void disconnect()\n  {\n    client_.disconnect();\n    // serverConn_.reset();\n  }\n\n private:\n  void teardown()\n  {\n    client_.setConnectionCallback(muduo::net::defaultConnectionCallback);\n    client_.setMessageCallback(muduo::net::defaultMessageCallback);\n    if (serverConn_)\n    {\n      serverConn_->setContext(boost::any());\n      serverConn_->shutdown();\n    }\n    clientConn_.reset();\n  }\n\n  void onClientConnection(const muduo::net::TcpConnectionPtr& conn)\n  {\n    using std::placeholders::_1;\n    using std::placeholders::_2;\n\n    LOG_DEBUG << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      conn->setTcpNoDelay(true);\n      conn->setHighWaterMarkCallback(\n          std::bind(&Tunnel::onHighWaterMarkWeak,\n                    std::weak_ptr<Tunnel>(shared_from_this()), kClient, _1, _2),\n          1024*1024);\n      serverConn_->setContext(conn);\n      serverConn_->startRead();\n      clientConn_ = conn;\n      if (serverConn_->inputBuffer()->readableBytes() > 0)\n      {\n        conn->send(serverConn_->inputBuffer());\n      }\n    }\n    else\n    {\n      teardown();\n    }\n  }\n\n  void onClientMessage(const muduo::net::TcpConnectionPtr& conn,\n                       muduo::net::Buffer* buf,\n                       muduo::Timestamp)\n  {\n    LOG_DEBUG << conn->name() << \" \" << buf->readableBytes();\n    if (serverConn_)\n    {\n      serverConn_->send(buf);\n    }\n    else\n    {\n      buf->retrieveAll();\n      abort();\n    }\n  }\n\n  enum ServerClient\n  {\n    kServer, kClient\n  };\n\n  void onHighWaterMark(ServerClient which,\n                       const muduo::net::TcpConnectionPtr& conn,\n                       size_t bytesToSent)\n  {\n    using std::placeholders::_1;\n\n    LOG_INFO << (which == kServer ? \"server\" : \"client\")\n             << \" onHighWaterMark \" << conn->name()\n             << \" bytes \" << bytesToSent;\n\n    if (which == kServer)\n    {\n      if (serverConn_->outputBuffer()->readableBytes() > 0)\n      {\n        clientConn_->stopRead();\n        serverConn_->setWriteCompleteCallback(\n            std::bind(&Tunnel::onWriteCompleteWeak,\n                      std::weak_ptr<Tunnel>(shared_from_this()), kServer, _1));\n      }\n    }\n    else\n    {\n      if (clientConn_->outputBuffer()->readableBytes() > 0)\n      {\n        serverConn_->stopRead();\n        clientConn_->setWriteCompleteCallback(\n            std::bind(&Tunnel::onWriteCompleteWeak,\n                      std::weak_ptr<Tunnel>(shared_from_this()), kClient, _1));\n      }\n    }\n  }\n\n  static void onHighWaterMarkWeak(const std::weak_ptr<Tunnel>& wkTunnel,\n                                  ServerClient which,\n                                  const muduo::net::TcpConnectionPtr& conn,\n                                  size_t bytesToSent)\n  {\n    std::shared_ptr<Tunnel> tunnel = wkTunnel.lock();\n    if (tunnel)\n    {\n      tunnel->onHighWaterMark(which, conn, bytesToSent);\n    }\n  }\n\n  void onWriteComplete(ServerClient which, const muduo::net::TcpConnectionPtr& conn)\n  {\n    LOG_INFO << (which == kServer ? \"server\" : \"client\")\n             << \" onWriteComplete \" << conn->name();\n    if (which == kServer)\n    {\n      clientConn_->startRead();\n      serverConn_->setWriteCompleteCallback(muduo::net::WriteCompleteCallback());\n    }\n    else\n    {\n      serverConn_->startRead();\n      clientConn_->setWriteCompleteCallback(muduo::net::WriteCompleteCallback());\n    }\n  }\n\n  static void onWriteCompleteWeak(const std::weak_ptr<Tunnel>& wkTunnel,\n                                  ServerClient which,\n                                  const muduo::net::TcpConnectionPtr& conn)\n  {\n    std::shared_ptr<Tunnel> tunnel = wkTunnel.lock();\n    if (tunnel)\n    {\n      tunnel->onWriteComplete(which, conn);\n    }\n  }\n\n private:\n  muduo::net::TcpClient client_;\n  muduo::net::TcpConnectionPtr serverConn_;\n  muduo::net::TcpConnectionPtr clientConn_;\n};\ntypedef std::shared_ptr<Tunnel> TunnelPtr;\n\n#endif  // MUDUO_EXAMPLES_SOCKS4A_TUNNEL_H\n"
  },
  {
    "path": "examples/sudoku/CMakeLists.txt",
    "content": "add_executable(sudoku_solver_basic server_basic.cc sudoku.cc)\ntarget_link_libraries(sudoku_solver_basic muduo_net)\n\nadd_executable(sudoku_solver_multiloop server_multiloop.cc sudoku.cc)\ntarget_link_libraries(sudoku_solver_multiloop muduo_net)\n\nadd_executable(sudoku_solver_threadpool server_threadpool.cc sudoku.cc)\ntarget_link_libraries(sudoku_solver_threadpool muduo_net)\n\nadd_executable(sudoku_solver_hybrid server_hybrid.cc sudoku.cc)\ntarget_link_libraries(sudoku_solver_hybrid muduo_inspect)\n\nadd_executable(sudoku_solver_prod server_prod.cc sudoku.cc)\ntarget_link_libraries(sudoku_solver_prod muduo_inspect)\n\n\nadd_executable(sudoku_client_batch batch.cc sudoku.cc)\ntarget_link_libraries(sudoku_client_batch muduo_net)\n\nadd_executable(sudoku_client_pipeline pipeline.cc sudoku.cc)\ntarget_link_libraries(sudoku_client_pipeline muduo_net)\n\nadd_executable(sudoku_loadtest loadtest.cc sudoku.cc)\ntarget_link_libraries(sudoku_loadtest muduo_net)\n\n\nif(BOOSTTEST_LIBRARY)\nadd_executable(sudoku_stat_unittest stat_unittest.cc)\ntarget_link_libraries(sudoku_stat_unittest muduo_base boost_unit_test_framework)\nendif()\n\n"
  },
  {
    "path": "examples/sudoku/batch.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <fstream>\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nbool verify(const string& result)\n{\n  return true;\n}\n\nvoid runLocal(std::istream& in)\n{\n  Timestamp start(Timestamp::now());\n  std::string line;\n  int count = 0;\n  int succeed = 0;\n  while (getline(in, line))\n  {\n    if (line.size() == implicit_cast<size_t>(kCells))\n    {\n      ++count;\n      if (verify(solveSudoku(line)))\n      {\n        ++succeed;\n      }\n    }\n  }\n  double elapsed = timeDifference(Timestamp::now(), start);\n  printf(\"%.3f sec, %.3f us per sudoku.\\n\", elapsed, 1000 * 1000 * elapsed / count);\n}\n\ntypedef std::vector<string> Input;\ntypedef std::shared_ptr<Input> InputPtr;\n\nInputPtr readInput(std::istream& in)\n{\n  InputPtr input(new Input);\n  std::string line;\n  while (getline(in, line))\n  {\n    if (line.size() == implicit_cast<size_t>(kCells))\n    {\n      input->push_back(line.c_str());\n    }\n  }\n  return input;\n}\n\ntypedef std::function<void(const string&, double, int)> DoneCallback;\n\nclass SudokuClient : noncopyable\n{\n public:\n  SudokuClient(EventLoop* loop,\n               const InetAddress& serverAddr,\n               const InputPtr& input,\n               const string& name,\n               const DoneCallback& cb\n               )\n    : name_(name),\n      client_(loop, serverAddr, name_),\n      input_(input),\n      cb_(cb),\n      count_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&SudokuClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&SudokuClient::onMessage, this, _1, _2, _3));\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      LOG_INFO << name_ << \" connected\";\n      start_ = Timestamp::now();\n      for (size_t i = 0; i < input_->size(); ++i)\n      {\n        LogStream buf;\n        buf << i+1 << \":\" << (*input_)[i] << \"\\r\\n\";\n        conn->send(buf.buffer().data(), buf.buffer().length());\n      }\n      LOG_INFO << name_ << \" sent requests\";\n    }\n    else\n    {\n      LOG_INFO << name_ << \" disconnected\";\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    //LOG_DEBUG << buf->retrieveAllAsString();\n\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string response(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        ++count_;\n        if (!verify(response))\n        {\n          LOG_ERROR << \"Bad response:\" << response;\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        LOG_ERROR << \"Line is too long!\";\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n\n    if (count_ == static_cast<int>(input_->size()))\n    {\n      LOG_INFO << name_ << \" done.\";\n      double elapsed = timeDifference(Timestamp::now(), start_);\n      cb_(name_, elapsed, count_);\n      conn->shutdown();\n    }\n  }\n\n  string name_;\n  TcpClient client_;\n  InputPtr input_;\n  DoneCallback cb_;\n  int count_;\n  Timestamp start_;\n};\n\nTimestamp g_start;\nint g_connections;\nint g_finished;\nEventLoop* g_loop;\n\nvoid done(const string& name, double elapsed, int count)\n{\n  LOG_INFO << name << \" \" << elapsed << \" seconds \"\n           << Fmt(\"%.3f\", 1000 * 1000 * elapsed / count)\n           << \" us per request.\";\n  ++g_finished;\n  if (g_finished == g_connections)\n  {\n    g_loop->runAfter(1.0, std::bind(&EventLoop::quit, g_loop));\n    double total = timeDifference(Timestamp::now(), g_start);\n    LOG_INFO << \"total \" << total << \" seconds, \"\n             << (total/g_connections) << \" seconds per client\";\n  }\n}\n\nvoid runClient(std::istream& in, const InetAddress& serverAddr, int conn)\n{\n  InputPtr input(readInput(in));\n  EventLoop loop;\n  g_loop = &loop;\n  g_connections = conn;\n\n  g_start = Timestamp::now();\n  std::vector<std::unique_ptr<SudokuClient>> clients;\n  for (int i = 0; i < conn; ++i)\n  {\n    Fmt f(\"client-%03d\", i+1);\n    string name(f.data(), f.length());\n    clients.emplace_back(new SudokuClient(&loop, serverAddr, input, name, done));\n    clients.back()->connect();\n  }\n\n  loop.loop();\n}\n\nint main(int argc, char* argv[])\n{\n  int conn = 1;\n  InetAddress serverAddr(\"127.0.0.1\", 9981);\n  const char* input = NULL;\n  bool local = true;\n  switch (argc)\n  {\n    case 4:\n      conn = atoi(argv[3]);\n      // FALL THROUGH\n    case 3:\n      serverAddr = InetAddress(argv[2], 9981);\n      local = false;\n      // FALL THROUGH\n    case 2:\n      input = argv[1];\n      break;\n    default:\n      printf(\"Usage: %s input server_ip [connections]\\n\", argv[0]);\n      return 0;\n  }\n\n  std::ifstream in(input);\n  if (in)\n  {\n    if (local)\n    {\n      runLocal(in);\n    }\n    else\n    {\n      runClient(in, serverAddr, conn);\n    }\n  }\n  else\n  {\n    printf(\"Cannot open %s\\n\", input);\n  }\n}\n"
  },
  {
    "path": "examples/sudoku/loadtest.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <fstream>\n#include <numeric>\n#include <unordered_map>\n\n#include \"examples/sudoku/percentile.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::vector<string> Input;\ntypedef std::shared_ptr<const Input> InputPtr;\n\nInputPtr readInput(std::istream& in)\n{\n  std::shared_ptr<Input> input(new Input);\n  std::string line;\n  while (getline(in, line))\n  {\n    if (line.size() == implicit_cast<size_t>(kCells))\n    {\n      input->push_back(line.c_str());\n    }\n  }\n  return input;\n}\n\nclass SudokuClient : noncopyable\n{\n public:\n  SudokuClient(EventLoop* loop,\n               const InetAddress& serverAddr,\n               const InputPtr& input,\n               const string& name,\n               bool nodelay)\n    : name_(name),\n      tcpNoDelay_(nodelay),\n      client_(loop, serverAddr, name_),\n      input_(input),\n      count_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&SudokuClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&SudokuClient::onMessage, this, _1, _2, _3));\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void send(int n)\n  {\n    assert(n > 0);\n    if (!conn_)\n      return;\n\n    Timestamp now(Timestamp::now());\n    for (int i = 0; i < n; ++i)\n    {\n      char buf[256];\n      const string& req = (*input_)[count_ % input_->size()];\n      int len = snprintf(buf, sizeof buf, \"%s-%08d:%s\\r\\n\",\n                         name_.c_str(), count_, req.c_str());\n      requests_.append(buf, len);\n      sendTime_[count_] = now;\n      ++count_;\n    }\n\n    conn_->send(&requests_);\n  }\n\n  void report(std::vector<int>* latency, int* infly)\n  {\n    latency->insert(latency->end(), latencies_.begin(), latencies_.end());\n    latencies_.clear();\n    *infly += static_cast<int>(sendTime_.size());\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      LOG_INFO << name_ << \" connected\";\n      if (tcpNoDelay_)\n        conn->setTcpNoDelay(true);\n      conn_ = conn;\n    }\n    else\n    {\n      LOG_INFO << name_ << \" disconnected\";\n      conn_.reset();\n      // FIXME: exit\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp recvTime)\n  {\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string response(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        if (!verify(response, recvTime))\n        {\n          LOG_ERROR << \"Bad response:\" << response;\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        LOG_ERROR << \"Line is too long!\";\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  bool verify(const string& response, Timestamp recvTime)\n  {\n    size_t colon = response.find(':');\n    if (colon != string::npos)\n    {\n      size_t dash = response.find('-');\n      if (dash != string::npos && dash < colon)\n      {\n        int id = atoi(response.c_str()+dash+1);\n        std::unordered_map<int, Timestamp>::iterator sendTime = sendTime_.find(id);\n        if (sendTime != sendTime_.end())\n        {\n          int64_t latency_us = recvTime.microSecondsSinceEpoch() - sendTime->second.microSecondsSinceEpoch();\n          latencies_.push_back(static_cast<int>(latency_us));\n          sendTime_.erase(sendTime);\n        }\n        else\n        {\n          LOG_ERROR << \"Unknown id \" << id << \" of \" << name_;\n        }\n      }\n    }\n    // FIXME\n    return true;\n  }\n\n  const string name_;\n  const bool tcpNoDelay_;\n  TcpClient client_;\n  TcpConnectionPtr conn_;\n  Buffer requests_;\n  const InputPtr input_;\n  int count_;\n  std::unordered_map<int, Timestamp> sendTime_;\n  std::vector<int> latencies_;\n};\n\nclass SudokuLoadtest : noncopyable\n{\n public:\n  SudokuLoadtest()\n    : count_(0),\n      ticks_(0),\n      sofar_(0)\n  {\n  }\n\n  void runClient(const InputPtr& input, const InetAddress& serverAddr, int rps, int conn, bool nodelay)\n  {\n    EventLoop loop;\n\n    for (int i = 0; i < conn; ++i)\n    {\n      Fmt f(\"c%04d\", i+1);\n      string name(f.data(), f.length());\n      clients_.emplace_back(new SudokuClient(&loop, serverAddr, input, name, nodelay));\n      clients_.back()->connect();\n    }\n\n    loop.runEvery(1.0 / kHz, std::bind(&SudokuLoadtest::tick, this, rps));\n    loop.runEvery(1.0, std::bind(&SudokuLoadtest::tock, this));\n    loop.loop();\n  }\n\n private:\n  void tick(int rps)\n  {\n    ++ticks_;\n    int64_t reqs = rps * ticks_ / kHz - sofar_;\n    sofar_ += reqs;\n\n    if (reqs > 0)\n    {\n      for (const auto& client : clients_)\n      {\n        client->send(static_cast<int>(reqs));\n      }\n    }\n  }\n\n  void tock()\n  {\n    std::vector<int> latencies;\n    int infly = 0;\n    for (const auto& client : clients_)\n    {\n      client->report(&latencies, &infly);\n    }\n\n    Percentile p(latencies, infly);\n    LOG_INFO << p.report();\n    char buf[64];\n    snprintf(buf, sizeof buf, \"r%04d\", count_);\n    p.save(latencies, buf);\n    ++count_;\n  }\n\n  std::vector<std::unique_ptr<SudokuClient>> clients_;\n  int count_;\n  int64_t ticks_;\n  int64_t sofar_;\n  static const int kHz = 100;\n};\n\nint main(int argc, char* argv[])\n{\n  int conn = 1;\n  int rps = 100;\n  bool nodelay = false;\n  InetAddress serverAddr(\"127.0.0.1\", 9981);\n  switch (argc)\n  {\n    case 6:\n      nodelay = string(argv[5]) == \"-n\";\n      // FALL THROUGH\n    case 5:\n      conn = atoi(argv[4]);\n      // FALL THROUGH\n    case 4:\n      rps = atoi(argv[3]);\n      // FALL THROUGH\n    case 3:\n      serverAddr = InetAddress(argv[2], 9981);\n      // FALL THROUGH\n    case 2:\n      break;\n    default:\n      printf(\"Usage: %s input server_ip [requests_per_second] [connections] [-n]\\n\", argv[0]);\n      return 0;\n  }\n\n  std::ifstream in(argv[1]);\n  if (in)\n  {\n    InputPtr input(readInput(in));\n    printf(\"%zd requests from %s\\n\", input->size(), argv[1]);\n    SudokuLoadtest test;\n    test.runClient(input, serverAddr, rps, conn, nodelay);\n  }\n  else\n  {\n    printf(\"Cannot open %s\\n\", argv[1]);\n  }\n}\n"
  },
  {
    "path": "examples/sudoku/percentile.h",
    "content": "\n// this is not a standalone header file\n\nclass Percentile\n{\n public:\n  Percentile(std::vector<int>& latencies, int infly)\n  {\n    stat << \"recv \" << muduo::Fmt(\"%6zd\", latencies.size()) << \" in-fly \" << infly;\n\n    if (!latencies.empty())\n    {\n      std::sort(latencies.begin(), latencies.end());\n      int min = latencies.front();\n      int max = latencies.back();\n      int sum = std::accumulate(latencies.begin(), latencies.end(), 0);\n      int mean = sum / static_cast<int>(latencies.size());\n      int median = getPercentile(latencies, 50);\n      int p90 = getPercentile(latencies, 90);\n      int p99 = getPercentile(latencies, 99);\n      stat << \" min \" << min\n           << \" max \" << max\n           << \" avg \" << mean\n           << \" median \" << median\n           << \" p90 \" << p90\n           << \" p99 \" << p99;\n    }\n  }\n\n  const muduo::LogStream::Buffer& report() const\n  {\n    return stat.buffer();\n  }\n\n  void save(const std::vector<int>& latencies, muduo::StringArg name) const\n  {\n    if (latencies.empty())\n      return;\n    muduo::FileUtil::AppendFile f(name);\n    f.append(\"# \", 2);\n    f.append(stat.buffer().data(), stat.buffer().length());\n    f.append(\"\\n\", 1);\n\n    const int kInterval = 5; // 5 us per bucket\n    int low = latencies.front() / kInterval * kInterval;\n    int count = 0;\n    int sum = 0;\n    const double total = static_cast<double>(latencies.size());\n    char buf[64];\n#ifndef NDEBUG\n    for (size_t i = 0; i < latencies.size(); ++i)\n    {\n      int n = snprintf(buf, sizeof buf, \"# %d\\n\", latencies[i]);\n      f.append(buf, n);\n    }\n#endif\n    // FIXME: improve this O(N) algorithm, maybe use lower_bound().\n    for (size_t i = 0; i < latencies.size(); ++i)\n    {\n      if (latencies[i] < low + kInterval)\n        ++count;\n      else\n      {\n        sum += count;\n        int n = snprintf(buf, sizeof buf, \"%4d %5d %5.2f\\n\", low, count, 100 * sum / total);\n        f.append(buf, n);\n        low = latencies[i] / kInterval * kInterval;\n        assert(latencies[i] < low + kInterval);\n        count = 1;\n      }\n    }\n    sum += count;\n    assert(sum == total);\n    int n = snprintf(buf, sizeof buf, \"%4d %5d %5.1f\\n\", low, count, 100 * sum / total);\n    f.append(buf, n);\n  }\n\n private:\n\n  static int getPercentile(const std::vector<int>& latencies, int percent)\n  {\n    // The Nearest Rank method\n    assert(!latencies.empty());\n    size_t idx = 0;\n    if (percent > 0)\n    {\n      idx = (latencies.size() * percent + 99) / 100 - 1;\n      assert(idx < latencies.size());\n    }\n    return latencies[idx];\n  }\n\n  muduo::LogStream stat;\n};\n"
  },
  {
    "path": "examples/sudoku/pipeline.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <fstream>\n#include <numeric>\n#include <unordered_map>\n\n#include \"examples/sudoku/percentile.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::vector<string> Input;\ntypedef std::shared_ptr<const Input> InputPtr;\n\nclass SudokuClient : noncopyable\n{\n public:\n  SudokuClient(EventLoop* loop,\n               const InetAddress& serverAddr,\n               const InputPtr& input,\n               const string& name,\n               int pipelines,\n               bool nodelay)\n    : name_(name),\n      pipelines_(pipelines),\n      tcpNoDelay_(nodelay),\n      client_(loop, serverAddr, name_),\n      input_(input),\n      count_(0)\n  {\n    client_.setConnectionCallback(\n        std::bind(&SudokuClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&SudokuClient::onMessage, this, _1, _2, _3));\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n\n  void report(std::vector<int>* latency, int* infly)\n  {\n    latency->insert(latency->end(), latencies_.begin(), latencies_.end());\n    latencies_.clear();\n    *infly += static_cast<int>(sendTime_.size());\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      LOG_INFO << name_ << \" connected\";\n      if (tcpNoDelay_)\n        conn->setTcpNoDelay(true);\n      conn_ = conn;\n      send(pipelines_);\n    }\n    else\n    {\n      LOG_INFO << name_ << \" disconnected\";\n      conn_.reset();\n      // FIXME: exit\n    }\n  }\n\n  void send(int n)\n  {\n    Timestamp now(Timestamp::now());\n    Buffer requests;\n    for (int i = 0; i < n; ++i)\n    {\n      char buf[256];\n      const string& req = (*input_)[count_ % input_->size()];\n      int len = snprintf(buf, sizeof buf, \"%s-%08d:%s\\r\\n\",\n                         name_.c_str(), count_, req.c_str());\n      requests.append(buf, len);\n      sendTime_[count_] = now;\n      ++count_;\n    }\n\n    conn_->send(&requests);\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp recvTime)\n  {\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string response(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        if (verify(response, recvTime))\n        {\n          send(1);\n        }\n        else\n        {\n          LOG_ERROR << \"Bad response:\" << response;\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        LOG_ERROR << \"Line is too long!\";\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  bool verify(const string& response, Timestamp recvTime)\n  {\n    size_t colon = response.find(':');\n    if (colon != string::npos)\n    {\n      size_t dash = response.find('-');\n      if (dash != string::npos && dash < colon)\n      {\n        int id = atoi(response.c_str()+dash+1);\n        std::unordered_map<int, Timestamp>::iterator sendTime = sendTime_.find(id);\n        if (sendTime != sendTime_.end())\n        {\n          int64_t latency_us = recvTime.microSecondsSinceEpoch() - sendTime->second.microSecondsSinceEpoch();\n          latencies_.push_back(static_cast<int>(latency_us));\n          sendTime_.erase(sendTime);\n        }\n        else\n        {\n          LOG_ERROR << \"Unknown id \" << id << \" of \" << name_;\n        }\n      }\n    }\n    // FIXME\n    return true;\n  }\n\n  const string name_;\n  const int pipelines_;\n  const bool tcpNoDelay_;\n  TcpClient client_;\n  TcpConnectionPtr conn_;\n  const InputPtr input_;\n  int count_;\n  std::unordered_map<int, Timestamp> sendTime_;\n  std::vector<int> latencies_;\n};\n\nvoid report(const std::vector<std::unique_ptr<SudokuClient>>& clients)\n{\n  static int count = 0;\n\n  std::vector<int> latencies;\n  int infly = 0;\n  for (const auto& client : clients)\n  {\n    client->report(&latencies, &infly);\n  }\n\n  Percentile p(latencies, infly);\n  LOG_INFO << p.report();\n  char buf[64];\n  snprintf(buf, sizeof buf, \"p%04d\", count);\n  p.save(latencies, buf);\n  ++count;\n}\n\nInputPtr readInput(std::istream& in)\n{\n  std::shared_ptr<Input> input(new Input);\n  std::string line;\n  while (getline(in, line))\n  {\n    if (line.size() == implicit_cast<size_t>(kCells))\n    {\n      input->push_back(line.c_str());\n    }\n  }\n  return input;\n}\n\nvoid runClient(const InputPtr& input,\n               const InetAddress& serverAddr,\n               int conn,\n               int pipelines,\n               bool nodelay)\n{\n  EventLoop loop;\n  std::vector<std::unique_ptr<SudokuClient>> clients;\n  for (int i = 0; i < conn; ++i)\n  {\n    Fmt f(\"c%04d\", i+1);\n    string name(f.data(), f.length());\n    clients.emplace_back(new SudokuClient(&loop, serverAddr, input, name, pipelines, nodelay));\n    clients.back()->connect();\n  }\n\n  loop.runEvery(1.0, std::bind(report, std::ref(clients)));\n  loop.loop();\n}\n\nint main(int argc, char* argv[])\n{\n  int conn = 1;\n  int pipelines = 1;\n  bool nodelay = false;\n  InetAddress serverAddr(\"127.0.0.1\", 9981);\n  switch (argc)\n  {\n    case 6:\n      nodelay = string(argv[5]) == \"-n\";\n      // FALL THROUGH\n    case 5:\n      pipelines = atoi(argv[4]);\n      // FALL THROUGH\n    case 4:\n      conn = atoi(argv[3]);\n      // FALL THROUGH\n    case 3:\n      serverAddr = InetAddress(argv[2], 9981);\n      // FALL THROUGH\n    case 2:\n      break;\n    default:\n      printf(\"Usage: %s input server_ip [connections] [pipelines] [-n]\\n\", argv[0]);\n      return 0;\n  }\n\n  std::ifstream in(argv[1]);\n  if (in)\n  {\n    InputPtr input(readInput(in));\n    printf(\"%zd requests from %s\\n\", input->size(), argv[1]);\n    runClient(input, serverAddr, conn, pipelines, nodelay);\n  }\n  else\n  {\n    printf(\"Cannot open %s\\n\", argv[1]);\n  }\n}\n"
  },
  {
    "path": "examples/sudoku/server_basic.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass SudokuServer\n{\n public:\n  SudokuServer(EventLoop* loop, const InetAddress& listenAddr)\n    : server_(loop, listenAddr, \"SudokuServer\"),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&SudokuServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    LOG_DEBUG << conn->name();\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string request(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        if (!processRequest(conn, request))\n        {\n          conn->send(\"Bad Request!\\r\\n\");\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        conn->send(\"Id too long!\\r\\n\");\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  bool processRequest(const TcpConnectionPtr& conn, const string& request)\n  {\n    string id;\n    string puzzle;\n    bool goodRequest = true;\n\n    string::const_iterator colon = find(request.begin(), request.end(), ':');\n    if (colon != request.end())\n    {\n      id.assign(request.begin(), colon);\n      puzzle.assign(colon+1, request.end());\n    }\n    else\n    {\n      puzzle = request;\n    }\n\n    if (puzzle.size() == implicit_cast<size_t>(kCells))\n    {\n      LOG_DEBUG << conn->name();\n      string result = solveSudoku(puzzle);\n      if (id.empty())\n      {\n        conn->send(result+\"\\r\\n\");\n      }\n      else\n      {\n        conn->send(id+\":\"+result+\"\\r\\n\");\n      }\n    }\n    else\n    {\n      goodRequest = false;\n    }\n    return goodRequest;\n  }\n\n  TcpServer server_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  SudokuServer server(&loop, listenAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/sudoku/server_hybrid.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"muduo/net/inspect/Inspector.h\"\n\n#include <boost/circular_buffer.hpp>\n\n//#include <stdio.h>\n//#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n#include \"examples/sudoku/stat.h\"\n\nclass SudokuServer : noncopyable\n{\n public:\n  SudokuServer(EventLoop* loop,\n               const InetAddress& listenAddr,\n               int numEventLoops,\n               int numThreads,\n               bool nodelay)\n    : server_(loop, listenAddr, \"SudokuServer\"),\n      threadPool_(),\n      numThreads_(numThreads),\n      tcpNoDelay_(nodelay),\n      startTime_(Timestamp::now()),\n      stat_(threadPool_),\n      inspectThread_(),\n      inspector_(inspectThread_.startLoop(), InetAddress(9982), \"sudoku-solver\")\n  {\n    LOG_INFO << \"Use \" << numEventLoops << \" IO threads.\";\n    LOG_INFO << \"TCP no delay \" << nodelay;\n\n    server_.setConnectionCallback(\n        std::bind(&SudokuServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numEventLoops);\n\n    inspector_.add(\"sudoku\", \"stats\", std::bind(&SudokuStat::report, &stat_),\n                   \"statistics of sudoku solver\");\n    inspector_.add(\"sudoku\", \"reset\", std::bind(&SudokuStat::reset, &stat_),\n                   \"reset statistics of sudoku solver\");\n  }\n\n  void start()\n  {\n    LOG_INFO << \"Starting \" << numThreads_ << \" computing threads.\";\n    threadPool_.start(numThreads_);\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected() && tcpNoDelay_)\n      conn->setTcpNoDelay(true);\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)\n  {\n    LOG_DEBUG << conn->name();\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string request(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        stat_.recordRequest();\n        if (!processRequest(conn, request, receiveTime))\n        {\n          conn->send(\"Bad Request!\\r\\n\");\n          conn->shutdown();\n          stat_.recordBadRequest();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        conn->send(\"Id too long!\\r\\n\");\n        conn->shutdown();\n        stat_.recordBadRequest();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  struct Request\n  {\n    string id;\n    string puzzle;\n    Timestamp receiveTime;\n  };\n\n  bool processRequest(const TcpConnectionPtr& conn, const string& request, Timestamp receiveTime)\n  {\n    Request req;\n    req.receiveTime = receiveTime;\n\n    string::const_iterator colon = find(request.begin(), request.end(), ':');\n    if (colon != request.end())\n    {\n      req.id.assign(request.begin(), colon);\n      req.puzzle.assign(colon+1, request.end());\n    }\n    else\n    {\n      // when using thread pool, an id must be provided in the request.\n      if (numThreads_ > 1)\n        return false;\n      req.puzzle = request;\n    }\n\n    if (req.puzzle.size() == implicit_cast<size_t>(kCells))\n    {\n      threadPool_.run(std::bind(&SudokuServer::solve, this, conn, req));\n      return true;\n    }\n    return false;\n  }\n\n  void solve(const TcpConnectionPtr& conn, const Request& req)\n  {\n    LOG_DEBUG << conn->name();\n    string result = solveSudoku(req.puzzle);\n    if (req.id.empty())\n    {\n      conn->send(result + \"\\r\\n\");\n    }\n    else\n    {\n      conn->send(req.id + \":\" + result + \"\\r\\n\");\n    }\n    stat_.recordResponse(Timestamp::now(), req.receiveTime, result != kNoSolution);\n  }\n\n  TcpServer server_;\n  ThreadPool threadPool_;\n  const int numThreads_;\n  const bool tcpNoDelay_;\n  const Timestamp startTime_;\n\n  SudokuStat stat_;\n  EventLoopThread inspectThread_;\n  Inspector inspector_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << argv[0] << \" [number of IO threads] [number of worker threads] [-n]\";\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  int numEventLoops = 0;\n  int numThreads = 0;\n  bool nodelay = false;\n  if (argc > 1)\n  {\n    numEventLoops = atoi(argv[1]);\n  }\n  if (argc > 2)\n  {\n    numThreads = atoi(argv[2]);\n  }\n  if (argc > 3 && string(argv[3]) == \"-n\")\n  {\n    nodelay = true;\n  }\n\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  SudokuServer server(&loop, listenAddr, numEventLoops, numThreads, nodelay);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/sudoku/server_multiloop.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass SudokuServer\n{\n public:\n  SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)\n    : server_(loop, listenAddr, \"SudokuServer\"),\n      numThreads_(numThreads),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&SudokuServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads_ << \" threads.\";\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    LOG_DEBUG << conn->name();\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string request(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        if (!processRequest(conn, request))\n        {\n          conn->send(\"Bad Request!\\r\\n\");\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        conn->send(\"Id too long!\\r\\n\");\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  bool processRequest(const TcpConnectionPtr& conn, const string& request)\n  {\n    string id;\n    string puzzle;\n    bool goodRequest = true;\n\n    string::const_iterator colon = find(request.begin(), request.end(), ':');\n    if (colon != request.end())\n    {\n      id.assign(request.begin(), colon);\n      puzzle.assign(colon+1, request.end());\n    }\n    else\n    {\n      puzzle = request;\n    }\n\n    if (puzzle.size() == implicit_cast<size_t>(kCells))\n    {\n      LOG_DEBUG << conn->name();\n      string result = solveSudoku(puzzle);\n      if (id.empty())\n      {\n        conn->send(result+\"\\r\\n\");\n      }\n      else\n      {\n        conn->send(id+\":\"+result+\"\\r\\n\");\n      }\n    }\n    else\n    {\n      goodRequest = false;\n    }\n    return goodRequest;\n  }\n\n  TcpServer server_;\n  int numThreads_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  int numThreads = 0;\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  SudokuServer server(&loop, listenAddr, numThreads);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/sudoku/server_prod.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"muduo/net/inspect/Inspector.h\"\n\n#include <boost/circular_buffer.hpp>\n\n//#include <stdio.h>\n//#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n#include \"examples/sudoku/stat.h\"\n\nclass SudokuServer : noncopyable\n{\n public:\n  SudokuServer(EventLoop* loop,\n               const InetAddress& listenAddr,\n               int numEventLoops,\n               int numThreads,\n               bool nodelay)\n    : server_(loop, listenAddr, \"SudokuServer\"),\n      threadPool_(),\n      numThreads_(numThreads),\n      tcpNoDelay_(nodelay),\n      startTime_(Timestamp::now()),\n      stat_(threadPool_),\n      inspectThread_(),\n      inspector_(inspectThread_.startLoop(), InetAddress(9982), \"sudoku-solver\")\n  {\n    LOG_INFO << \"Use \" << numEventLoops << \" IO threads.\";\n    LOG_INFO << \"TCP no delay \" << nodelay;\n\n    server_.setConnectionCallback(\n        std::bind(&SudokuServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numEventLoops);\n\n    inspector_.add(\"sudoku\", \"stats\", std::bind(&SudokuStat::report, &stat_),\n                   \"statistics of sudoku solver\");\n    inspector_.add(\"sudoku\", \"reset\", std::bind(&SudokuStat::reset, &stat_),\n                   \"reset statistics of sudoku solver\");\n  }\n\n  void start()\n  {\n    LOG_INFO << \"Starting \" << numThreads_ << \" computing threads.\";\n    threadPool_.start(numThreads_);\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (conn->connected())\n    {\n      if (tcpNoDelay_)\n        conn->setTcpNoDelay(true);\n      conn->setHighWaterMarkCallback(\n          std::bind(&SudokuServer::highWaterMark, this, _1, _2), 5 * 1024 * 1024);\n      bool throttle = false;\n      conn->setContext(throttle);\n    }\n  }\n\n  void highWaterMark(const TcpConnectionPtr& conn, size_t tosend)\n  {\n    LOG_WARN << conn->name() << \" high water mark \" << tosend;\n    if (tosend < 10 * 1024 * 1024)\n    {\n      conn->setHighWaterMarkCallback(\n          std::bind(&SudokuServer::highWaterMark, this, _1, _2), 10 * 1024 * 1024);\n      conn->setWriteCompleteCallback(std::bind(&SudokuServer::writeComplete, this, _1));\n      bool throttle = true;\n      conn->setContext(throttle);\n    }\n    else\n    {\n      conn->send(\"Bad Request!\\r\\n\");\n      conn->shutdown();  // FIXME: forceClose() ?\n      stat_.recordBadRequest();\n    }\n  }\n\n  void writeComplete(const TcpConnectionPtr& conn)\n  {\n    LOG_INFO << conn->name() << \" write complete\";\n    conn->setHighWaterMarkCallback(\n        std::bind(&SudokuServer::highWaterMark, this, _1, _2), 5 * 1024 * 1024);\n    conn->setWriteCompleteCallback(WriteCompleteCallback());\n    bool throttle = false;\n    conn->setContext(throttle);\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)\n  {\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string request(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        stat_.recordRequest();\n        if (!processRequest(conn, request, receiveTime))\n        {\n          conn->send(\"Bad Request!\\r\\n\");\n          conn->shutdown();\n          stat_.recordBadRequest();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        conn->send(\"Id too long!\\r\\n\");\n        conn->shutdown();\n        stat_.recordBadRequest();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  struct Request\n  {\n    string id;\n    string puzzle;\n    Timestamp receiveTime;\n  };\n\n  bool processRequest(const TcpConnectionPtr& conn, const string& request, Timestamp receiveTime)\n  {\n    Request req;\n    req.receiveTime = receiveTime;\n\n    string::const_iterator colon = find(request.begin(), request.end(), ':');\n    if (colon != request.end())\n    {\n      req.id.assign(request.begin(), colon);\n      req.puzzle.assign(colon+1, request.end());\n    }\n    else\n    {\n      // when using thread pool, an id must be provided in the request.\n      if (numThreads_ > 1)\n        return false;\n      req.puzzle = request;\n    }\n\n    if (req.puzzle.size() == implicit_cast<size_t>(kCells))\n    {\n      bool throttle = boost::any_cast<bool>(conn->getContext());\n      if (threadPool_.queueSize() < 1000 * 1000 && !throttle)\n      {\n        threadPool_.run(std::bind(&SudokuServer::solve, this, conn, req));\n      }\n      else\n      {\n        if (req.id.empty())\n        {\n          conn->send(\"ServerTooBusy\\r\\n\");\n        }\n        else\n        {\n          conn->send(req.id + \":ServerTooBusy\\r\\n\");\n        }\n        stat_.recordDroppedRequest();\n      }\n      return true;\n    }\n    return false;\n  }\n\n  void solve(const TcpConnectionPtr& conn, const Request& req)\n  {\n    LOG_DEBUG << conn->name();\n    string result = solveSudoku(req.puzzle);\n    if (req.id.empty())\n    {\n      conn->send(result + \"\\r\\n\");\n    }\n    else\n    {\n      conn->send(req.id + \":\" + result + \"\\r\\n\");\n    }\n    stat_.recordResponse(Timestamp::now(), req.receiveTime, result != kNoSolution);\n  }\n\n  TcpServer server_;\n  ThreadPool threadPool_;\n  const int numThreads_;\n  const bool tcpNoDelay_;\n  const Timestamp startTime_;\n\n  SudokuStat stat_;\n  EventLoopThread inspectThread_;\n  Inspector inspector_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << argv[0] << \" [number of IO threads] [number of worker threads] [-n]\";\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  int numEventLoops = 0;\n  int numThreads = 0;\n  bool nodelay = false;\n  if (argc > 1)\n  {\n    numEventLoops = atoi(argv[1]);\n  }\n  if (argc > 2)\n  {\n    numThreads = atoi(argv[2]);\n  }\n  if (argc > 3 && string(argv[3]) == \"-n\")\n  {\n    nodelay = true;\n  }\n\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  SudokuServer server(&loop, listenAddr, numEventLoops, numThreads, nodelay);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/sudoku/server_threadpool.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass SudokuServer\n{\n public:\n  SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)\n    : server_(loop, listenAddr, \"SudokuServer\"),\n      numThreads_(numThreads),\n      startTime_(Timestamp::now())\n  {\n    server_.setConnectionCallback(\n        std::bind(&SudokuServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&SudokuServer::onMessage, this, _1, _2, _3));\n  }\n\n  void start()\n  {\n    LOG_INFO << \"starting \" << numThreads_ << \" threads.\";\n    threadPool_.start(numThreads_);\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    LOG_DEBUG << conn->name();\n    size_t len = buf->readableBytes();\n    while (len >= kCells + 2)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        string request(buf->peek(), crlf);\n        buf->retrieveUntil(crlf + 2);\n        len = buf->readableBytes();\n        if (!processRequest(conn, request))\n        {\n          conn->send(\"Bad Request!\\r\\n\");\n          conn->shutdown();\n          break;\n        }\n      }\n      else if (len > 100) // id + \":\" + kCells + \"\\r\\n\"\n      {\n        conn->send(\"Id too long!\\r\\n\");\n        conn->shutdown();\n        break;\n      }\n      else\n      {\n        break;\n      }\n    }\n  }\n\n  bool processRequest(const TcpConnectionPtr& conn, const string& request)\n  {\n    string id;\n    string puzzle;\n    bool goodRequest = true;\n\n    string::const_iterator colon = find(request.begin(), request.end(), ':');\n    if (colon != request.end())\n    {\n      id.assign(request.begin(), colon);\n      puzzle.assign(colon+1, request.end());\n    }\n    else\n    {\n      puzzle = request;\n    }\n\n    if (puzzle.size() == implicit_cast<size_t>(kCells))\n    {\n      threadPool_.run(std::bind(&solve, conn, puzzle, id));\n    }\n    else\n    {\n      goodRequest = false;\n    }\n    return goodRequest;\n  }\n\n  static void solve(const TcpConnectionPtr& conn,\n                    const string& puzzle,\n                    const string& id)\n  {\n    LOG_DEBUG << conn->name();\n    string result = solveSudoku(puzzle);\n    if (id.empty())\n    {\n      conn->send(result+\"\\r\\n\");\n    }\n    else\n    {\n      conn->send(id+\":\"+result+\"\\r\\n\");\n    }\n  }\n\n  TcpServer server_;\n  ThreadPool threadPool_;\n  int numThreads_;\n  Timestamp startTime_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  int numThreads = 0;\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  InetAddress listenAddr(9981);\n  SudokuServer server(&loop, listenAddr, numThreads);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "examples/sudoku/stat.h",
    "content": "// This is not a standalone header\n\nclass SudokuStat : muduo::noncopyable\n{\n public:\n  SudokuStat(const ThreadPool& pool)\n    : pool_(pool),\n      lastSecond_(0),\n      requests_(kSeconds),\n      latencies_(kSeconds),\n      totalRequests_(0),\n      totalResponses_(0),\n      totalSolved_(0),\n      badRequests_(0),\n      droppedRequests_(0),\n      totalLatency_(0),\n      badLatency_(0)\n  {\n  }\n\n  string report() const\n  {\n    LogStream result;\n    size_t queueSize = pool_.queueSize();\n    result << \"task_queue_size \" << queueSize << '\\n';\n\n    {\n    MutexLockGuard lock(mutex_);\n    result << \"total_requests \" << totalRequests_ << '\\n';\n    result << \"total_responses \" << totalResponses_ << '\\n';\n    result << \"total_solved \" << totalSolved_ << '\\n';\n    result << \"bad_requests \" << badRequests_ << '\\n';\n    result << \"dropped_requests \" << droppedRequests_ << '\\n';\n    result << \"latency_sum_us \" << totalLatency_ << '\\n';\n    if (badLatency_ > 0)\n    {\n      result << \"bad_latency\" << badLatency_ << '\\n';\n    }\n\n    result << \"last_second \" << lastSecond_ << '\\n';\n    int64_t requests = 0;\n    result << \"requests_per_second\";\n    for (size_t i = 0; i < requests_.size(); ++i)\n    {\n      requests += requests_[i];\n      result << ' ' << requests_[i];\n    }\n    result << '\\n';\n    result << \"requests_60s \" << requests << '\\n';\n\n    int64_t latency = 0;\n    result << \"latency_sum_us_per_second\";\n    for (size_t i = 0; i < latencies_.size(); ++i)\n    {\n      latency += latencies_[i];\n      result << ' ' << latencies_[i];\n    }\n    result << '\\n';\n    result << \"latency_sum_us_60s \" << latency << '\\n';\n    int64_t latencyAvg60s = requests == 0 ? 0 : latency / requests;\n    result << \"latency_us_60s \" << latencyAvg60s << '\\n';\n    int64_t latencyAvg = totalResponses_ == 0 ? 0 : totalLatency_ / totalResponses_;\n    result << \"latency_us_avg \" << latencyAvg << '\\n';\n    }\n    return result.buffer().toString();\n  }\n\n  string reset()\n  {\n    {\n    MutexLockGuard lock(mutex_);\n    lastSecond_ = 0;\n    requests_.clear();\n    latencies_.clear();\n    totalRequests_ = 0;\n    totalResponses_ = 0;\n    totalSolved_ = 0;\n    badRequests_ = 0;\n    totalLatency_ = 0;\n    badLatency_ = 0;\n    }\n    return \"reset done.\";\n  }\n\n  void recordResponse(Timestamp now, Timestamp receive, bool solved)\n  {\n    const time_t second = now.secondsSinceEpoch();\n    const int64_t elapsed_us = now.microSecondsSinceEpoch() - receive.microSecondsSinceEpoch();\n    MutexLockGuard lock(mutex_);\n    assert(requests_.size() == latencies_.size());\n    ++totalResponses_;\n    if (solved)\n      ++totalSolved_;\n    if (elapsed_us < 0)\n    {\n      ++badLatency_;\n      return;\n    }\n    totalLatency_ += elapsed_us;\n\n    const time_t firstSecond = lastSecond_ - static_cast<ssize_t>(requests_.size()) + 1;\n    if (lastSecond_ == second)\n    {\n      // the most common case\n      ++requests_.back();\n      latencies_.back() += elapsed_us;\n    }\n    else if (lastSecond_ + 1 == second || lastSecond_ == 0)\n    {\n      // next second\n      lastSecond_ = second;\n      requests_.push_back(0);\n      latencies_.push_back(0);\n      ++requests_.back();\n      latencies_.back() += elapsed_us;\n    }\n    else if (second > lastSecond_)\n    {\n      // jump ahead\n      if (second < lastSecond_ + kSeconds)\n      {\n        // eg. lastSecond_ == 100, second < 160\n        while (lastSecond_ < second)\n        {\n          requests_.push_back(0);\n          latencies_.push_back(0);\n          ++lastSecond_;\n        }\n      }\n      else\n      {\n        // eg. lastSecond_ == 100, second >= 160\n        requests_.clear();\n        latencies_.clear();\n        lastSecond_ = second;\n        requests_.push_back(0);\n        latencies_.push_back(0);\n      }\n      ++requests_.back();\n      latencies_.back() += elapsed_us;\n    }\n    else if (second >= firstSecond)\n    {\n      // jump backwards\n      // eg. lastSecond_ = 150, size = 10, second > 140\n      // FIXME: if second > lastSecond_ - kSeconds, push_front()\n\n      size_t idx = second - firstSecond;\n      assert(idx < requests_.size());\n      ++requests_[idx];\n      latencies_[idx] += elapsed_us;\n    }\n    else\n    {\n      assert(second < firstSecond);\n      // discard\n      // eg. lastSecond_ = 150, size = 10, second <= 140\n    }\n    assert(requests_.size() == latencies_.size());\n  }\n\n  void recordRequest()\n  {\n    MutexLockGuard lock(mutex_);\n    ++totalRequests_;\n  }\n\n  void recordBadRequest()\n  {\n    MutexLockGuard lock(mutex_);\n    ++badRequests_;\n  }\n\n  void recordDroppedRequest()\n  {\n    MutexLockGuard lock(mutex_);\n    ++droppedRequests_;\n  }\n\n private:\n  const ThreadPool& pool_;  // only for ThreadPool::queueSize()\n  mutable MutexLock mutex_;\n  // invariant:\n  // 0. requests_.size() == latencies_.size()\n  // 1. if lastSecond_ > 0, requests_.back() is for that second\n  // 2. requests_.front() is for second (last second - size() + 1)\n  time_t lastSecond_;\n  boost::circular_buffer<int64_t> requests_;\n  boost::circular_buffer<int64_t> latencies_;\n  int64_t totalRequests_, totalResponses_, totalSolved_, badRequests_, droppedRequests_, totalLatency_, badLatency_;\n  // FIXME int128_t for totalLatency_;\n\n  static const int kSeconds = 60;\n};\n\n"
  },
  {
    "path": "examples/sudoku/stat_unittest.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/ThreadPool.h\"\n\n#include <boost/circular_buffer.hpp>\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\nusing namespace muduo;\n\n#include \"examples/sudoku/stat.h\"\n\n#include <stdio.h>\n\nBOOST_AUTO_TEST_CASE(testSudokuStatSameSecond)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  for (int i = 0; i < 100; ++i)\n  {\n    time_t start = 1234567890;\n    Timestamp recv = Timestamp::fromUnixTime(start, 0);\n    Timestamp send = Timestamp::fromUnixTime(start, i);\n    s.recordResponse(send, recv, i % 3 != 0);\n  }\n  printf(\"same second:\\n%s\\n\", s.report().c_str());\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatNextSecond)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  Timestamp recv = Timestamp::fromUnixTime(start, 0);\n  Timestamp send = addTime(recv, 0.002);\n  for (int i = 0; i < 10000; ++i)\n  {\n    s.recordResponse(send, recv, true);\n    recv = addTime(send, 0.01);\n    send = addTime(recv, 0.02);\n  }\n  printf(\"next second:\\n%s\\n\", s.report().c_str());\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatFuzz)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  srand(static_cast<unsigned>(time(NULL)));\n  for (int i = 0; i < 10000; ++i)\n  {\n    Timestamp recv = Timestamp::fromUnixTime(start, 0);\n    Timestamp send = Timestamp::fromUnixTime(start, 200);\n    s.recordResponse(send, recv, true);\n    int jump = (rand() % 200) - 100;\n    // printf(\"%4d \", jump);\n    start += jump;\n  }\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatJumpAhead5)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  Timestamp recv = Timestamp::fromUnixTime(start, 0);\n  Timestamp send = Timestamp::fromUnixTime(start, 200);\n  s.recordResponse(send, recv, true);\n\n  recv = addTime(recv, 4);\n  send = addTime(send, 5);\n  s.recordResponse(send, recv, true);\n  printf(\"jump ahead 5 seconds:\\n%s\\n\", s.report().c_str());\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatJumpAhead59)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  Timestamp recv = Timestamp::fromUnixTime(start, 0);\n  Timestamp send = Timestamp::fromUnixTime(start, 200);\n  s.recordResponse(send, recv, true);\n\n  recv = addTime(recv, 55);\n  send = addTime(send, 59);\n  s.recordResponse(send, recv, true);\n  printf(\"jump ahead 59 seconds:\\n%s\\n\", s.report().c_str());\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatJumpAhead60)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  Timestamp recv = Timestamp::fromUnixTime(start, 0);\n  Timestamp send = Timestamp::fromUnixTime(start, 200);\n  s.recordResponse(send, recv, true);\n\n  recv = addTime(recv, 58);\n  send = addTime(send, 60);\n  s.recordResponse(send, recv, true);\n  printf(\"jump ahead 60 seconds:\\n%s\\n\", s.report().c_str());\n}\n\nBOOST_AUTO_TEST_CASE(testSudokuStatJumpBack3)\n{\n  ThreadPool p;\n  SudokuStat s(p);\n\n  time_t start = 1234567890;\n  Timestamp recv = Timestamp::fromUnixTime(start, 0);\n  Timestamp send = Timestamp::fromUnixTime(start, 200);\n  s.recordResponse(send, recv, true);\n\n  recv = addTime(recv, 9);\n  send = addTime(send, 10);\n  s.recordResponse(send, recv, true);\n\n  recv = addTime(recv, -4);\n  send = addTime(send, -3);\n  s.recordResponse(send, recv, true);\n\n  printf(\"jump back 3 seconds:\\n%s\\n\", s.report().c_str());\n}\n\n"
  },
  {
    "path": "examples/sudoku/sudoku.cc",
    "content": "#include \"examples/sudoku/sudoku.h\"\n\n#include <vector>\n#include <assert.h>\n#include <string.h>\n\nusing namespace muduo;\n\n// Dancing links algorithm by Donald E. Knuth\n// www-cs-faculty.stanford.edu/~uno/papers/dancing-color.ps.gz\n\nstruct Node;\ntypedef Node Column;\nstruct Node\n{\n    Node* left;\n    Node* right;\n    Node* up;\n    Node* down;\n    Column* col;\n    int name;\n    int size;\n};\n\nconst int kMaxNodes = 1 + 81*4 + 9*9*9*4;\n// const int kMaxColumns = 400;\nconst int kRow = 100, kCol = 200, kBox = 300;\nextern const char kNoSolution[] = \"NoSolution\";\n\nclass SudokuSolver\n{\n public:\n    SudokuSolver(int board[kCells])\n      : inout_(board),\n        cur_node_(0)\n    {\n        stack_.reserve(100);\n\n        root_ = new_column();\n        root_->left = root_->right = root_;\n        memZero(columns_, sizeof(columns_));\n\n        bool rows[kCells][10] = { {false} };\n        bool cols[kCells][10] = { {false} };\n        bool boxes[kCells][10] = { {false} };\n\n        for (int i = 0; i < kCells; ++i) {\n            int row = i / 9;\n            int col = i % 9;\n            int box = row/3*3 + col/3;\n            int val = inout_[i];\n            rows[row][val] = true;\n            cols[col][val] = true;\n            boxes[box][val] = true;\n        }\n\n        for (int i = 0; i < kCells; ++i) {\n            if (inout_[i] == 0) {\n                append_column(i);\n            }\n        }\n\n        for (int i = 0; i < 9; ++i) {\n            for (int v = 1; v < 10; ++v) {\n                if (!rows[i][v])\n                    append_column(get_row_col(i, v));\n                if (!cols[i][v])\n                    append_column(get_col_col(i, v));\n                if (!boxes[i][v])\n                    append_column(get_box_col(i, v));\n            }\n        }\n\n        for (int i = 0; i < kCells; ++i) {\n            if (inout_[i] == 0) {\n                int row = i / 9;\n                int col = i % 9;\n                int box = row/3*3 + col/3;\n                //int val = inout[i];\n                for (int v = 1; v < 10; ++v) {\n                    if (!(rows[row][v] || cols[col][v] || boxes[box][v])) {\n                        Node* n0 = new_row(i);\n                        Node* nr = new_row(get_row_col(row, v));\n                        Node* nc = new_row(get_col_col(col, v));\n                        Node* nb = new_row(get_box_col(box, v));\n                        put_left(n0, nr);\n                        put_left(n0, nc);\n                        put_left(n0, nb);\n                    }\n                }\n            }\n        }\n    }\n\n    bool solve()\n    {\n        if (root_->left == root_) {\n            for (size_t i = 0; i < stack_.size(); ++i) {\n                Node* n = stack_[i];\n                int cell = -1;\n                int val = -1;\n                while (cell == -1 || val == -1) {\n                    if (n->name < 100)\n                        cell = n->name;\n                    else\n                        val = n->name % 10;\n                    n = n->right;\n                }\n\n                //assert(cell != -1 && val != -1);\n                inout_[cell] = val;\n            }\n            return true;\n        }\n\n        Column* const col = get_min_column();\n        cover(col);\n        for (Node* row = col->down; row != col; row = row->down) {\n            stack_.push_back(row);\n            for (Node* j = row->right; j != row; j = j->right) {\n                cover(j->col);\n            }\n            if (solve()) {\n                return true;\n            }\n            stack_.pop_back();\n            for (Node* j = row->left; j != row; j = j->left) {\n                uncover(j->col);\n            }\n        }\n        uncover(col);\n        return false;\n    }\n\n private:\n\n    Column* root_;\n    int*    inout_;\n    Column* columns_[400];\n    std::vector<Node*> stack_;\n    Node    nodes_[kMaxNodes];\n    int     cur_node_;\n\n    Column* new_column(int n = 0)\n    {\n        assert(cur_node_ < kMaxNodes);\n        Column* c = &nodes_[cur_node_++];\n        memZero(c, sizeof(Column));\n        c->left = c;\n        c->right = c;\n        c->up = c;\n        c->down = c;\n        c->col = c;\n        c->name = n;\n        return c;\n    }\n\n    void append_column(int n)\n    {\n        assert(columns_[n] == NULL);\n\n        Column* c = new_column(n);\n        put_left(root_, c);\n        columns_[n] = c;\n    }\n\n    Node* new_row(int col)\n    {\n        assert(columns_[col] != NULL);\n        assert(cur_node_ < kMaxNodes);\n\n        Node* r = &nodes_[cur_node_++];\n\n        //Node* r = new Node;\n        memZero(r, sizeof(Node));\n        r->left = r;\n        r->right = r;\n        r->up = r;\n        r->down = r;\n        r->name = col;\n        r->col = columns_[col];\n        put_up(r->col, r);\n        return r;\n    }\n\n    int get_row_col(int row, int val)\n    {\n        return kRow+row*10+val;\n    }\n\n    int get_col_col(int col, int val)\n    {\n        return kCol+col*10+val;\n    }\n\n    int get_box_col(int box, int val)\n    {\n        return kBox+box*10+val;\n    }\n\n    Column* get_min_column()\n    {\n        Column* c = root_->right;\n        int min_size = c->size;\n        if (min_size > 1) {\n            for (Column* cc = c->right; cc != root_; cc = cc->right) {\n                if (min_size > cc->size) {\n                    c = cc;\n                    min_size = cc->size;\n                    if (min_size <= 1)\n                        break;\n                }\n            }\n        }\n        return c;\n    }\n\n    void cover(Column* c)\n    {\n        c->right->left = c->left;\n        c->left->right = c->right;\n        for (Node* row = c->down; row != c; row = row->down) {\n            for (Node* j = row->right; j != row; j = j->right) {\n                j->down->up = j->up;\n                j->up->down = j->down;\n                j->col->size--;\n            }\n        }\n    }\n\n    void uncover(Column* c)\n    {\n        for (Node* row = c->up; row != c; row = row->up) {\n            for (Node* j = row->left; j != row; j = j->left) {\n                j->col->size++;\n                j->down->up = j;\n                j->up->down = j;\n            }\n        }\n        c->right->left = c;\n        c->left->right = c;\n    }\n\n    void put_left(Column* old, Column* nnew)\n    {\n        nnew->left = old->left;\n        nnew->right = old;\n        old->left->right = nnew;\n        old->left = nnew;\n    }\n\n    void put_up(Column* old, Node* nnew)\n    {\n        nnew->up = old->up;\n        nnew->down = old;\n        old->up->down = nnew;\n        old->up = nnew;\n        old->size++;\n        nnew->col = old;\n    }\n};\n\nstring solveSudoku(const StringPiece& puzzle)\n{\n  assert(puzzle.size() == kCells);\n\n  string result = kNoSolution;\n\n  int board[kCells] = { 0 };\n  bool valid = true;\n  for (int i = 0; i < kCells; ++i)\n  {\n    board[i] = puzzle[i] - '0';\n    valid = valid && (0 <= board[i] && board[i] <= 9);\n  }\n\n  if (valid)\n  {\n    SudokuSolver s(board);\n    if (s.solve())\n    {\n      result.clear();\n      result.resize(kCells);\n      for (int i = 0; i < kCells; ++i)\n      {\n        result[i] = static_cast<char>(board[i] + '0');\n      }\n    }\n  }\n  return result;\n}\n\n"
  },
  {
    "path": "examples/sudoku/sudoku.h",
    "content": "#ifndef MUDUO_EXAMPLES_SUDOKU_SUDOKU_H\n#define MUDUO_EXAMPLES_SUDOKU_SUDOKU_H\n\n\n#include \"muduo/base/Types.h\"\n#include \"muduo/base/StringPiece.h\"\n\nmuduo::string solveSudoku(const muduo::StringPiece& puzzle);\nconst int kCells = 81;\nextern const char kNoSolution[];\n\n#endif  // MUDUO_EXAMPLES_SUDOKU_SUDOKU_H\n"
  },
  {
    "path": "examples/twisted/finger/CMakeLists.txt",
    "content": "add_executable(twisted_finger01 finger01.cc)\ntarget_link_libraries(twisted_finger01 muduo_net)\n\nadd_executable(twisted_finger02 finger02.cc)\ntarget_link_libraries(twisted_finger02 muduo_net)\n\nadd_executable(twisted_finger03 finger03.cc)\ntarget_link_libraries(twisted_finger03 muduo_net)\n\nadd_executable(twisted_finger04 finger04.cc)\ntarget_link_libraries(twisted_finger04 muduo_net)\n\nadd_executable(twisted_finger05 finger05.cc)\ntarget_link_libraries(twisted_finger05 muduo_net)\n\nadd_executable(twisted_finger06 finger06.cc)\ntarget_link_libraries(twisted_finger06 muduo_net)\n\nadd_executable(twisted_finger07 finger07.cc)\ntarget_link_libraries(twisted_finger07 muduo_net)\n\n"
  },
  {
    "path": "examples/twisted/finger/README",
    "content": "The Finger protocol, RFC 1288.\n\n\"Finger is based on the Transmission Control Protocol, using TCP port\n79 decimal (117 octal).  The local host opens a TCP connection to a\nremote host on the Finger port.  An RUIP becomes available on the\nremote end of the connection to process the request.  The local host\nsends the RUIP a one line query based upon the Finger query\nspecification, and waits for the RUIP to respond.  The RUIP receives\nand processes the query, returns an answer, then initiates the close\nof the connection.  The local host receives the answer and the close\nsignal, then proceeds closing its end of the connection.\"\n\n"
  },
  {
    "path": "examples/twisted/finger/finger01.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  EventLoop loop;\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger02.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger03.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn->shutdown();\n  }\n}\n\nint main()\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.setConnectionCallback(onConnection);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger04.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onMessage(const TcpConnectionPtr& conn,\n               Buffer* buf,\n               Timestamp receiveTime)\n{\n  if (buf->findCRLF())\n  {\n    conn->shutdown();\n  }\n}\n\nint main()\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.setMessageCallback(onMessage);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger05.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid onMessage(const TcpConnectionPtr& conn,\n               Buffer* buf,\n               Timestamp receiveTime)\n{\n  if (buf->findCRLF())\n  {\n    conn->send(\"No such user\\r\\n\");\n    conn->shutdown();\n  }\n}\n\nint main()\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.setMessageCallback(onMessage);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger06.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <map>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::map<string, string> UserMap;\nUserMap users;\n\nstring getUser(const string& user)\n{\n  string result = \"No such user\";\n  UserMap::iterator it = users.find(user);\n  if (it != users.end())\n  {\n    result = it->second;\n  }\n  return result;\n}\n\nvoid onMessage(const TcpConnectionPtr& conn,\n               Buffer* buf,\n               Timestamp receiveTime)\n{\n  const char* crlf = buf->findCRLF();\n  if (crlf)\n  {\n    string user(buf->peek(), crlf);\n    conn->send(getUser(user) + \"\\r\\n\");\n    buf->retrieveUntil(crlf + 2);\n    conn->shutdown();\n  }\n}\n\nint main()\n{\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.setMessageCallback(onMessage);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/twisted/finger/finger07.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include <map>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\ntypedef std::map<string, string> UserMap;\nUserMap users;\n\nstring getUser(const string& user)\n{\n  string result = \"No such user\";\n  UserMap::iterator it = users.find(user);\n  if (it != users.end())\n  {\n    result = it->second;\n  }\n  return result;\n}\n\nvoid onMessage(const TcpConnectionPtr& conn,\n               Buffer* buf,\n               Timestamp receiveTime)\n{\n  const char* crlf = buf->findCRLF();\n  if (crlf)\n  {\n    string user(buf->peek(), crlf);\n    conn->send(getUser(user) + \"\\r\\n\");\n    buf->retrieveUntil(crlf + 2);\n    conn->shutdown();\n  }\n}\n\nint main()\n{\n  users[\"schen\"] = \"Happy and well\";\n  EventLoop loop;\n  TcpServer server(&loop, InetAddress(1079), \"Finger\");\n  server.setMessageCallback(onMessage);\n  server.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "examples/wordcount/CMakeLists.txt",
    "content": "add_executable(wordcount_hasher hasher.cc)\ntarget_link_libraries(wordcount_hasher muduo_net)\n\nadd_executable(wordcount_receiver receiver.cc)\ntarget_link_libraries(wordcount_receiver muduo_net)\n"
  },
  {
    "path": "examples/wordcount/README",
    "content": "A distributed word counting example.\n\nA hasher shards <word,count> to multiple receivers by hash(word).\n\nA receiver collects <word,count> from multiple hashers\nand writes the result to disk.\n\nExample run with 3 hashers and 4 receivers:\n1. run 4 receivers on 4 machines, namely ip1:port1, ip2:port2, ip3:port3, ip4:port4.\n   a. on ip1, bin/wordcount_receiver port1 3\n   b. on ip2, bin/wordcount_receiver port2 3\n   c. on ip3, bin/wordcount_receiver port3 3\n   d. on ip4, bin/wordcount_receiver port4 3\n2. run 3 hashers on 3 machines.\n   a. on ip1, bin/wordcount_hasher 'ip1:port1,ip2:port2,ip3:port3,ip4:port4' input1\n   b. on ip2, bin/wordcount_hasher 'ip1:port1,ip2:port2,ip3:port3,ip4:port4' input2\n   c. on ip3, bin/wordcount_hasher 'ip1:port1,ip2:port2,ip3:port3,ip4:port4' input3 input4\n3. wait all hashers and receivers exit.\n\n"
  },
  {
    "path": "examples/wordcount/gen.py",
    "content": "#!/usr/bin/python\n\nimport random\n\nwords = 1000000\nword_len = 5\nalphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'\n\noutput = open('random_words', 'w')\nfor x in xrange(words):\n\tarr = [random.choice(alphabet) for i in range(word_len)]\n\tword = ''.join(arr)\n\toutput.write(word)\n\toutput.write('\\n')\n"
  },
  {
    "path": "examples/wordcount/hash.h",
    "content": "#ifndef MUDUO_EXAMPLES_WORDCOUNT_HASH_H\n#define MUDUO_EXAMPLES_WORDCOUNT_HASH_H\n\n#include <unordered_map>\n\ntypedef std::unordered_map<muduo::string, int64_t> WordCountMap;\n\n#endif  // MUDUO_EXAMPLES_WORDCOUNT_HASH_H\n"
  },
  {
    "path": "examples/wordcount/hasher.cc",
    "content": "#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/TcpClient.h\"\n\n#include <boost/tokenizer.hpp>\n\n#include \"examples/wordcount/hash.h\"\n\n#include <fstream>\n#include <iostream>\n\n#include <stdio.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nsize_t g_batchSize = 65536;\nconst size_t kMaxHashSize = 10 * 1000 * 1000;\n\nclass SendThrottler : muduo::noncopyable\n{\n public:\n  SendThrottler(EventLoop* loop, const InetAddress& addr)\n    : client_(loop, addr, \"Sender\"),\n      connectLatch_(1),\n      disconnectLatch_(1),\n      cond_(mutex_),\n      congestion_(false)\n  {\n    LOG_INFO << \"SendThrottler [\" << addr.toIpPort() << \"]\";\n    client_.setConnectionCallback(\n        std::bind(&SendThrottler::onConnection, this, _1));\n  }\n\n  void connect()\n  {\n    client_.connect();\n    connectLatch_.wait();\n  }\n\n  void disconnect()\n  {\n    if (buffer_.readableBytes() > 0)\n    {\n      LOG_DEBUG << \"send \" << buffer_.readableBytes() << \" bytes\";\n      conn_->send(&buffer_);\n    }\n    conn_->shutdown();\n    disconnectLatch_.wait();\n  }\n\n  void send(const string& word, int64_t count)\n  {\n    buffer_.append(word);\n    // FIXME: use LogStream\n    char buf[64];\n    snprintf(buf, sizeof buf, \"\\t%\" PRId64 \"\\r\\n\", count);\n    buffer_.append(buf);\n    if (buffer_.readableBytes() >= g_batchSize)\n    {\n      throttle();\n      LOG_TRACE << \"send \" << buffer_.readableBytes();\n      conn_->send(&buffer_);\n    }\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    if (conn->connected())\n    {\n      conn->setHighWaterMarkCallback(\n          std::bind(&SendThrottler::onHighWaterMark, this), 1024*1024);\n      conn->setWriteCompleteCallback(\n          std::bind(&SendThrottler::onWriteComplete, this));\n\n      conn_ = conn;\n      connectLatch_.countDown();\n    }\n    else\n    {\n      conn_.reset();\n      disconnectLatch_.countDown();\n    }\n  }\n\n  void onHighWaterMark()\n  {\n    MutexLockGuard lock(mutex_);\n    congestion_ = true;\n  }\n\n  void onWriteComplete()\n  {\n    MutexLockGuard lock(mutex_);\n    bool oldCong = congestion_;\n    congestion_ = false;\n    if (oldCong)\n    {\n      cond_.notify();\n    }\n  }\n\n  void throttle()\n  {\n    MutexLockGuard lock(mutex_);\n    while (congestion_)\n    {\n      LOG_DEBUG << \"wait \";\n      cond_.wait();\n    }\n  }\n\n  TcpClient client_;\n  TcpConnectionPtr conn_;\n  CountDownLatch connectLatch_;\n  CountDownLatch disconnectLatch_;\n  Buffer buffer_;\n\n  MutexLock mutex_;\n  Condition cond_;\n  bool congestion_;\n};\n\nclass WordCountSender : muduo::noncopyable\n{\n public:\n  explicit WordCountSender(const std::string& receivers);\n\n  void connectAll()\n  {\n    for (size_t i = 0; i < buckets_.size(); ++i)\n    {\n      buckets_[i]->connect();\n    }\n    LOG_INFO << \"All connected\";\n  }\n\n  void disconnectAll()\n  {\n    for (size_t i = 0; i < buckets_.size(); ++i)\n    {\n      buckets_[i]->disconnect();\n    }\n    LOG_INFO << \"All disconnected\";\n  }\n\n  void processFile(const char* filename);\n\n private:\n  EventLoopThread loopThread_;\n  EventLoop* loop_;\n  std::vector<std::unique_ptr<SendThrottler>> buckets_;\n};\n\nWordCountSender::WordCountSender(const std::string& receivers)\n  : loop_(loopThread_.startLoop())\n{\n  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;\n  boost::char_separator<char> sep(\", \");\n  tokenizer tokens(receivers, sep);\n  for (tokenizer::iterator tok_iter = tokens.begin();\n       tok_iter != tokens.end(); ++tok_iter)\n  {\n    std::string ipport = *tok_iter;\n    size_t colon = ipport.find(':');\n    if (colon != std::string::npos)\n    {\n      uint16_t port = static_cast<uint16_t>(atoi(&ipport[colon+1]));\n      InetAddress addr(ipport.substr(0, colon), port);\n      buckets_.emplace_back(new SendThrottler(loop_, addr));\n    }\n    else\n    {\n      assert(0 && \"Invalid address\");\n    }\n  }\n}\n\nvoid WordCountSender::processFile(const char* filename)\n{\n  LOG_INFO << \"processFile \" << filename;\n  WordCountMap wordcounts;\n  // FIXME: use mmap to read file\n  std::ifstream in(filename);\n  string word;\n  // FIXME: make local hash optional.\n  std::hash<string> hash;\n  while (in)\n  {\n    wordcounts.clear();\n    while (in >> word)\n    {\n      wordcounts[word] += 1;\n      if (wordcounts.size() > kMaxHashSize)\n      {\n        break;\n      }\n    }\n\n    LOG_INFO << \"send \" << wordcounts.size() << \" records\";\n    for (WordCountMap::iterator it = wordcounts.begin();\n         it != wordcounts.end(); ++it)\n    {\n      size_t idx = hash(it->first) % buckets_.size();\n      buckets_[idx]->send(it->first, it->second);\n    }\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc < 3)\n  {\n    printf(\"Usage: %s addresses_of_receivers input_file1 [input_file2]* \\n\", argv[0]);\n    printf(\"Example: %s 'ip1:port1,ip2:port2,ip3:port3' input_file1 input_file2 \\n\", argv[0]);\n  }\n  else\n  {\n    const char* batchSize = ::getenv(\"BATCH_SIZE\");\n    if (batchSize)\n    {\n      g_batchSize = atoi(batchSize);\n    }\n    WordCountSender sender(argv[1]);\n    sender.connectAll();\n    for (int i = 2; i < argc; ++i)\n    {\n      sender.processFile(argv[i]);\n    }\n    sender.disconnectAll();\n  }\n}\n"
  },
  {
    "path": "examples/wordcount/receiver.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n\n#include \"examples/wordcount/hash.h\"\n\n#include <fstream>\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nclass WordCountReceiver : muduo::noncopyable\n{\n public:\n  WordCountReceiver(EventLoop* loop, const InetAddress& listenAddr)\n    : loop_(loop),\n      server_(loop, listenAddr, \"WordCountReceiver\"),\n      senders_(0)\n  {\n    server_.setConnectionCallback(\n         std::bind(&WordCountReceiver::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&WordCountReceiver::onMessage, this, _1, _2, _3));\n  }\n\n  void start(int senders)\n  {\n    LOG_INFO << \"start \" << senders << \" senders\";\n    senders_ = senders;\n    wordcounts_.clear();\n    server_.start();\n  }\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_DEBUG << conn->peerAddress().toIpPort() << \" -> \"\n              << conn->localAddress().toIpPort() << \" is \"\n              << (conn->connected() ? \"UP\" : \"DOWN\");\n    if (!conn->connected())\n    {\n      if (--senders_ == 0)\n      {\n        output();\n        loop_->quit();\n      }\n    }\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n  {\n    const char* crlf = NULL;\n    while ( (crlf = buf->findCRLF()) != NULL)\n    {\n      // string request(buf->peek(), crlf);\n      // printf(\"%s\\n\", request.c_str());\n      const char* tab = std::find(buf->peek(), crlf, '\\t');\n      if (tab != crlf)\n      {\n        string word(buf->peek(), tab);\n        int64_t cnt = atoll(tab);\n        wordcounts_[word] += cnt;\n      }\n      else\n      {\n        LOG_ERROR << \"Wrong format, no tab found\";\n        conn->shutdown();\n      }\n      buf->retrieveUntil(crlf + 2);\n    }\n  }\n\n  void output()\n  {\n    LOG_INFO << \"Writing shard\";\n    std::ofstream out(\"shard\");\n    for (WordCountMap::iterator it = wordcounts_.begin();\n         it != wordcounts_.end(); ++it)\n    {\n      out << it->first << '\\t' << it->second << '\\n';\n    }\n  }\n\n  EventLoop* loop_;\n  TcpServer server_;\n  int senders_;\n  WordCountMap wordcounts_;\n};\n\nint main(int argc, char* argv[])\n{\n  if (argc < 3)\n  {\n    printf(\"Usage: %s listen_port number_of_senders\\n\", argv[0]);\n  }\n  else\n  {\n    EventLoop loop;\n    int port = atoi(argv[1]);\n    InetAddress addr(static_cast<uint16_t>(port));\n    WordCountReceiver receiver(&loop, addr);\n    receiver.start(atoi(argv[2]));\n    loop.loop();\n  }\n}\n"
  },
  {
    "path": "examples/wordcount/slowsink.py",
    "content": "#!/usr/bin/python\n\nimport os, socket, sys, time\n\nhost = ''\nport = 2007\n\nif len(sys.argv) > 1:\n\tmps = float(sys.argv[1])\nelse:\n\tmps = 1.0\nbps = mps * 1000000\nBUFSIZE = int(bps/10) # sleep 100ms at full speed\n\nprint \"Mbytes/s =\", mps\n\nif len(sys.argv) > 3:\n\thost = sys.argv[2]\n\tport = int(sys.argv[3])\n\tprint \"connecting to %s:%d\" % (host, port)\nelse:\n\tprint \"listening on port\", port\n\nif host:\n\tclient_socket = socket.create_connection((host, port))\n\tprint \"connected to\", client_socket.getpeername()\nelse:\n\tlisten_address = (\"\", port)\n\tserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\tserver_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n\tserver_socket.bind(listen_address)\n\tserver_socket.listen(5)\n\n\t(client_socket, client_address) = server_socket.accept()\n\tprint \"got connection from\", client_address\n\nstart = time.time()\ntotal_size = 0\ndot = bps\n\nwhile True:\n\tdata = client_socket.recv(BUFSIZE)\n\tif data:\n\t\tsize = len(data)\n\t\ttotal_size += size\n\t\tif total_size >= dot:\n\t\t\tdot += bps\n\t\t\tsys.stdout.write('.')\n\t\t\tsys.stdout.flush()\n\t\ttime.sleep(size / bps)\n\telse:\n\t\tprint \"\\ndisconnect\"\n\t\tclient_socket.close()\n\t\tbreak\n\nend = time.time()\nelapsed = end - start\nprint \"elapsed seconds %.3f\" % elapsed\nprint \"total bytes\", total_size\nprint \"throughput bytes/s %.2f\" % (total_size / elapsed)\nprint \"throughput Mbytes/s %.3f\" % (total_size / elapsed / 1000000)\n\n"
  },
  {
    "path": "examples/zeromq/CMakeLists.txt",
    "content": "add_executable(zeromq_local_lat local_lat.cc)\ntarget_link_libraries(zeromq_local_lat muduo_net)\n\nadd_executable(zeromq_remote_lat remote_lat.cc)\ntarget_link_libraries(zeromq_remote_lat muduo_net)\n\n"
  },
  {
    "path": "examples/zeromq/README",
    "content": "\nlocal_lat.cc  : echo server with LengthHeaderCodec\nremote_lat.cc : echo client with LengthHeaderCodec\n"
  },
  {
    "path": "examples/zeromq/local_lat.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpServer.h\"\n#include \"examples/asio/chat/codec.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing std::placeholders::_1;\nusing std::placeholders::_2;\nusing std::placeholders::_3;\nusing muduo::get_pointer;\n\nbool g_tcpNoDelay = false;\n\nvoid onConnection(const muduo::net::TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn->setTcpNoDelay(g_tcpNoDelay);\n  }\n}\n\nvoid onStringMessage(LengthHeaderCodec* codec,\n                     const muduo::net::TcpConnectionPtr& conn,\n                     const muduo::string& message,\n                     muduo::Timestamp)\n{\n  codec->send(get_pointer(conn), message);\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc > 1)\n  {\n    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));\n    g_tcpNoDelay = argc > 2 ? atoi(argv[2]) : false;\n    int threadCount = argc > 3 ? atoi(argv[3]) : 0;\n\n    LOG_INFO << \"pid = \" << getpid() << \", listen port = \" << port;\n    // muduo::Logger::setLogLevel(muduo::Logger::WARN);\n    muduo::net::EventLoop loop;\n    muduo::net::InetAddress listenAddr(port);\n    muduo::net::TcpServer server(&loop, listenAddr, \"PingPong\");\n    LengthHeaderCodec codec(std::bind(onStringMessage, &codec, _1, _2, _3));\n\n    server.setConnectionCallback(onConnection);\n    server.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec, _1, _2, _3));\n\n    if (threadCount > 1)\n    {\n      server.setThreadNum(threadCount);\n    }\n\n    server.start();\n\n    loop.loop();\n  }\n  else\n  {\n    fprintf(stderr, \"Usage: %s listen_port [tcp_no_delay [threads]]\\n\", argv[0]);\n  }\n}\n"
  },
  {
    "path": "examples/zeromq/remote_lat.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n#include \"examples/asio/chat/codec.h\"\n\n#include <stdio.h>\n\nusing std::placeholders::_1;\nusing std::placeholders::_2;\nusing std::placeholders::_3;\nusing muduo::get_pointer;\n\nbool g_tcpNoDelay = false;\nint g_msgSize = 0;\nint g_totalMsgs = 0;\nint g_msgCount = 0;\nmuduo::string g_message;\nmuduo::Timestamp g_start;\n\nvoid onConnection(LengthHeaderCodec* codec, const muduo::net::TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    LOG_INFO << \"connected\";\n    g_start = muduo::Timestamp::now();\n    conn->setTcpNoDelay(g_tcpNoDelay);\n    codec->send(get_pointer(conn), g_message);\n  }\n  else\n  {\n    LOG_INFO << \"disconnected\";\n    muduo::net::EventLoop::getEventLoopOfCurrentThread()->quit();\n  }\n}\n\nvoid onStringMessage(LengthHeaderCodec* codec,\n                     const muduo::net::TcpConnectionPtr& conn,\n                     const muduo::string& message,\n                     muduo::Timestamp)\n{\n  if (message.size() != static_cast<size_t>(g_msgSize))\n  {\n    abort();\n  }\n\n  ++g_msgCount;\n\n  if (g_msgCount < g_totalMsgs)\n  {\n    codec->send(get_pointer(conn), message);\n  }\n  else\n  {\n    muduo::Timestamp end = muduo::Timestamp::now();\n    LOG_INFO << \"done\";\n    double elapsed = timeDifference(end, g_start);\n    LOG_INFO << g_msgSize << \" message bytes\";\n    LOG_INFO << g_msgCount << \" round-trips\";\n    LOG_INFO << elapsed << \" seconds\";\n    LOG_INFO << muduo::Fmt(\"%.3f\", g_msgCount / elapsed) << \" round-trips per second\";\n    LOG_INFO << muduo::Fmt(\"%.3f\", (1000000 * elapsed / g_msgCount / 2))\n             << \" latency [us]\";\n    LOG_INFO << muduo::Fmt(\"%.3f\", (g_msgSize * g_msgCount / elapsed / 1024 / 1024))\n             << \" band width [MiB/s]\";\n    conn->shutdown();\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  if (argc > 3)\n  {\n    const char* ip = argv[1];\n    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));\n    g_msgSize = atoi(argv[3]);\n    g_message.assign(g_msgSize, 'H');\n    g_totalMsgs = argc > 4 ? atoi(argv[4]) : 10000;\n    g_tcpNoDelay = argc > 5 ? atoi(argv[5]) : false;\n\n    muduo::net::EventLoop loop;\n    muduo::net::InetAddress serverAddr(ip, port);\n    muduo::net::TcpClient client(&loop, serverAddr, \"Client\");\n    LengthHeaderCodec codec(std::bind(onStringMessage, &codec, _1, _2, _3));\n    client.setConnectionCallback(\n        std::bind(onConnection, &codec, _1));\n    client.setMessageCallback(\n        std::bind(&LengthHeaderCodec::onMessage, &codec, _1, _2, _3));\n    client.connect();\n    loop.loop();\n  }\n  else\n  {\n    fprintf(stderr, \"Usage: %s server_ip server_port msg_size\", argv[0]);\n    fprintf(stderr, \" [msg_count [tcp_no_delay]]\\n\");\n  }\n}\n"
  },
  {
    "path": "muduo/base/AsyncLogging.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/AsyncLogging.h\"\n#include \"muduo/base/LogFile.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\n\nAsyncLogging::AsyncLogging(const string& basename,\n                           off_t rollSize,\n                           int flushInterval)\n  : flushInterval_(flushInterval),\n    running_(false),\n    basename_(basename),\n    rollSize_(rollSize),\n    thread_(std::bind(&AsyncLogging::threadFunc, this), \"Logging\"),\n    latch_(1),\n    mutex_(),\n    cond_(mutex_),\n    currentBuffer_(new Buffer),\n    nextBuffer_(new Buffer),\n    buffers_()\n{\n  currentBuffer_->bzero();\n  nextBuffer_->bzero();\n  buffers_.reserve(16);\n}\n\nvoid AsyncLogging::append(const char* logline, int len)\n{\n  muduo::MutexLockGuard lock(mutex_);\n  if (currentBuffer_->avail() > len)\n  {\n    currentBuffer_->append(logline, len);\n  }\n  else\n  {\n    buffers_.push_back(std::move(currentBuffer_));\n\n    if (nextBuffer_)\n    {\n      currentBuffer_ = std::move(nextBuffer_);\n    }\n    else\n    {\n      currentBuffer_.reset(new Buffer); // Rarely happens\n    }\n    currentBuffer_->append(logline, len);\n    cond_.notify();\n  }\n}\n\nvoid AsyncLogging::threadFunc()\n{\n  assert(running_ == true);\n  latch_.countDown();\n  LogFile output(basename_, rollSize_, false);\n  BufferPtr newBuffer1(new Buffer);\n  BufferPtr newBuffer2(new Buffer);\n  newBuffer1->bzero();\n  newBuffer2->bzero();\n  BufferVector buffersToWrite;\n  buffersToWrite.reserve(16);\n  while (running_)\n  {\n    assert(newBuffer1 && newBuffer1->length() == 0);\n    assert(newBuffer2 && newBuffer2->length() == 0);\n    assert(buffersToWrite.empty());\n\n    {\n      muduo::MutexLockGuard lock(mutex_);\n      if (buffers_.empty())  // unusual usage!\n      {\n        cond_.waitForSeconds(flushInterval_);\n      }\n      buffers_.push_back(std::move(currentBuffer_));\n      currentBuffer_ = std::move(newBuffer1);\n      buffersToWrite.swap(buffers_);\n      if (!nextBuffer_)\n      {\n        nextBuffer_ = std::move(newBuffer2);\n      }\n    }\n\n    assert(!buffersToWrite.empty());\n\n    if (buffersToWrite.size() > 25)\n    {\n      char buf[256];\n      snprintf(buf, sizeof buf, \"Dropped log messages at %s, %zd larger buffers\\n\",\n               Timestamp::now().toFormattedString().c_str(),\n               buffersToWrite.size()-2);\n      fputs(buf, stderr);\n      output.append(buf, static_cast<int>(strlen(buf)));\n      buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());\n    }\n\n    for (const auto& buffer : buffersToWrite)\n    {\n      // FIXME: use unbuffered stdio FILE ? or use ::writev ?\n      output.append(buffer->data(), buffer->length());\n    }\n\n    if (buffersToWrite.size() > 2)\n    {\n      // drop non-bzero-ed buffers, avoid trashing\n      buffersToWrite.resize(2);\n    }\n\n    if (!newBuffer1)\n    {\n      assert(!buffersToWrite.empty());\n      newBuffer1 = std::move(buffersToWrite.back());\n      buffersToWrite.pop_back();\n      newBuffer1->reset();\n    }\n\n    if (!newBuffer2)\n    {\n      assert(!buffersToWrite.empty());\n      newBuffer2 = std::move(buffersToWrite.back());\n      buffersToWrite.pop_back();\n      newBuffer2->reset();\n    }\n\n    buffersToWrite.clear();\n    output.flush();\n  }\n  output.flush();\n}\n\n"
  },
  {
    "path": "muduo/base/AsyncLogging.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_ASYNCLOGGING_H\n#define MUDUO_BASE_ASYNCLOGGING_H\n\n#include \"muduo/base/BlockingQueue.h\"\n#include \"muduo/base/BoundedBlockingQueue.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/LogStream.h\"\n\n#include <atomic>\n#include <vector>\n\nnamespace muduo\n{\n\nclass AsyncLogging : noncopyable\n{\n public:\n\n  AsyncLogging(const string& basename,\n               off_t rollSize,\n               int flushInterval = 3);\n\n  ~AsyncLogging()\n  {\n    if (running_)\n    {\n      stop();\n    }\n  }\n\n  void append(const char* logline, int len);\n\n  void start()\n  {\n    running_ = true;\n    thread_.start();\n    latch_.wait();\n  }\n\n  void stop() NO_THREAD_SAFETY_ANALYSIS\n  {\n    running_ = false;\n    cond_.notify();\n    thread_.join();\n  }\n\n private:\n\n  void threadFunc();\n\n  typedef muduo::detail::FixedBuffer<muduo::detail::kLargeBuffer> Buffer;\n  typedef std::vector<std::unique_ptr<Buffer>> BufferVector;\n  typedef BufferVector::value_type BufferPtr;\n\n  const int flushInterval_;\n  std::atomic<bool> running_;\n  const string basename_;\n  const off_t rollSize_;\n  muduo::Thread thread_;\n  muduo::CountDownLatch latch_;\n  muduo::MutexLock mutex_;\n  muduo::Condition cond_ GUARDED_BY(mutex_);\n  BufferPtr currentBuffer_ GUARDED_BY(mutex_);\n  BufferPtr nextBuffer_ GUARDED_BY(mutex_);\n  BufferVector buffers_ GUARDED_BY(mutex_);\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_ASYNCLOGGING_H\n"
  },
  {
    "path": "muduo/base/Atomic.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_ATOMIC_H\n#define MUDUO_BASE_ATOMIC_H\n\n#include \"muduo/base/noncopyable.h\"\n\n#include <stdint.h>\n\nnamespace muduo\n{\n\nnamespace detail\n{\ntemplate<typename T>\nclass AtomicIntegerT : noncopyable\n{\n public:\n  AtomicIntegerT()\n    : value_(0)\n  {\n  }\n\n  // uncomment if you need copying and assignment\n  //\n  // AtomicIntegerT(const AtomicIntegerT& that)\n  //   : value_(that.get())\n  // {}\n  //\n  // AtomicIntegerT& operator=(const AtomicIntegerT& that)\n  // {\n  //   getAndSet(that.get());\n  //   return *this;\n  // }\n\n  T get()\n  {\n    // in gcc >= 4.7: __atomic_load_n(&value_, __ATOMIC_SEQ_CST)\n    return __sync_val_compare_and_swap(&value_, 0, 0);\n  }\n\n  T getAndAdd(T x)\n  {\n    // in gcc >= 4.7: __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)\n    return __sync_fetch_and_add(&value_, x);\n  }\n\n  T addAndGet(T x)\n  {\n    return getAndAdd(x) + x;\n  }\n\n  T incrementAndGet()\n  {\n    return addAndGet(1);\n  }\n\n  T decrementAndGet()\n  {\n    return addAndGet(-1);\n  }\n\n  void add(T x)\n  {\n    getAndAdd(x);\n  }\n\n  void increment()\n  {\n    incrementAndGet();\n  }\n\n  void decrement()\n  {\n    decrementAndGet();\n  }\n\n  T getAndSet(T newValue)\n  {\n    // in gcc >= 4.7: __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST)\n    return __sync_lock_test_and_set(&value_, newValue);\n  }\n\n private:\n  volatile T value_;\n};\n}  // namespace detail\n\ntypedef detail::AtomicIntegerT<int32_t> AtomicInt32;\ntypedef detail::AtomicIntegerT<int64_t> AtomicInt64;\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_ATOMIC_H\n"
  },
  {
    "path": "muduo/base/BUILD.bazel",
    "content": "cc_library(\n    name = \"base\",\n    srcs = [\n        \"AsyncLogging.cc\",\n        \"Condition.cc\",\n        \"CountDownLatch.cc\",\n        \"CurrentThread.cc\",\n        \"Date.cc\",\n        \"Exception.cc\",\n        \"FileUtil.cc\",\n        \"LogFile.cc\",\n        \"LogStream.cc\",\n        \"Logging.cc\",\n        \"ProcessInfo.cc\",\n        \"Thread.cc\",\n        \"ThreadPool.cc\",\n        \"TimeZone.cc\",\n        \"Timestamp.cc\",\n    ],\n    hdrs = glob([\"*.h\"]),\n    linkopts = [\"-pthread\"],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "muduo/base/BlockingQueue.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_BLOCKINGQUEUE_H\n#define MUDUO_BASE_BLOCKINGQUEUE_H\n\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/Mutex.h\"\n\n#include <deque>\n#include <assert.h>\n\nnamespace muduo\n{\n\ntemplate<typename T>\nclass BlockingQueue : noncopyable\n{\n public:\n  using queue_type = std::deque<T>;\n\n  BlockingQueue()\n    : mutex_(),\n      notEmpty_(mutex_),\n      queue_()\n  {\n  }\n\n  void put(const T& x)\n  {\n    MutexLockGuard lock(mutex_);\n    queue_.push_back(x);\n    notEmpty_.notify(); // wait morphing saves us\n    // http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/\n  }\n\n  void put(T&& x)\n  {\n    MutexLockGuard lock(mutex_);\n    queue_.push_back(std::move(x));\n    notEmpty_.notify();\n  }\n\n  T take()\n  {\n    MutexLockGuard lock(mutex_);\n    // always use a while-loop, due to spurious wakeup\n    while (queue_.empty())\n    {\n      notEmpty_.wait();\n    }\n    assert(!queue_.empty());\n    T front(std::move(queue_.front()));\n    queue_.pop_front();\n    return front;\n  }\n\n  queue_type drain()\n  {\n    std::deque<T> queue;\n    {\n      MutexLockGuard lock(mutex_);\n      queue = std::move(queue_);\n      assert(queue_.empty());\n    }\n    return queue;\n  }\n\n  size_t size() const\n  {\n    MutexLockGuard lock(mutex_);\n    return queue_.size();\n  }\n\n private:\n  mutable MutexLock mutex_;\n  Condition         notEmpty_ GUARDED_BY(mutex_);\n  queue_type        queue_ GUARDED_BY(mutex_);\n};  // __attribute__ ((aligned (64)));\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_BLOCKINGQUEUE_H\n"
  },
  {
    "path": "muduo/base/BoundedBlockingQueue.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H\n#define MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H\n\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/Mutex.h\"\n\n#include <boost/circular_buffer.hpp>\n#include <assert.h>\n\nnamespace muduo\n{\n\ntemplate<typename T>\nclass BoundedBlockingQueue : noncopyable\n{\n public:\n  explicit BoundedBlockingQueue(int maxSize)\n    : mutex_(),\n      notEmpty_(mutex_),\n      notFull_(mutex_),\n      queue_(maxSize)\n  {\n  }\n\n  void put(const T& x)\n  {\n    MutexLockGuard lock(mutex_);\n    while (queue_.full())\n    {\n      notFull_.wait();\n    }\n    assert(!queue_.full());\n    queue_.push_back(x);\n    notEmpty_.notify();\n  }\n\n  void put(T&& x)\n  {\n    MutexLockGuard lock(mutex_);\n    while (queue_.full())\n    {\n      notFull_.wait();\n    }\n    assert(!queue_.full());\n    queue_.push_back(std::move(x));\n    notEmpty_.notify();\n  }\n\n  T take()\n  {\n    MutexLockGuard lock(mutex_);\n    while (queue_.empty())\n    {\n      notEmpty_.wait();\n    }\n    assert(!queue_.empty());\n    T front(std::move(queue_.front()));\n    queue_.pop_front();\n    notFull_.notify();\n    return front;\n  }\n\n  bool empty() const\n  {\n    MutexLockGuard lock(mutex_);\n    return queue_.empty();\n  }\n\n  bool full() const\n  {\n    MutexLockGuard lock(mutex_);\n    return queue_.full();\n  }\n\n  size_t size() const\n  {\n    MutexLockGuard lock(mutex_);\n    return queue_.size();\n  }\n\n  size_t capacity() const\n  {\n    MutexLockGuard lock(mutex_);\n    return queue_.capacity();\n  }\n\n private:\n  mutable MutexLock          mutex_;\n  Condition                  notEmpty_ GUARDED_BY(mutex_);\n  Condition                  notFull_ GUARDED_BY(mutex_);\n  boost::circular_buffer<T>  queue_ GUARDED_BY(mutex_);\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H\n"
  },
  {
    "path": "muduo/base/CMakeLists.txt",
    "content": "set(base_SRCS\n  AsyncLogging.cc\n  Condition.cc\n  CountDownLatch.cc\n  CurrentThread.cc\n  Date.cc\n  Exception.cc\n  FileUtil.cc\n  LogFile.cc\n  Logging.cc\n  LogStream.cc\n  ProcessInfo.cc\n  Timestamp.cc\n  Thread.cc\n  ThreadPool.cc\n  TimeZone.cc\n  )\n\nadd_library(muduo_base ${base_SRCS})\ntarget_link_libraries(muduo_base pthread rt)\n\n#add_library(muduo_base_cpp11 ${base_SRCS})\n#target_link_libraries(muduo_base_cpp11 pthread rt)\n#set_target_properties(muduo_base_cpp11 PROPERTIES COMPILE_FLAGS \"-std=c++0x\")\n\ninstall(TARGETS muduo_base DESTINATION lib)\n#install(TARGETS muduo_base_cpp11 DESTINATION lib)\n\nfile(GLOB HEADERS \"*.h\")\ninstall(FILES ${HEADERS} DESTINATION include/muduo/base)\n\nif(MUDUO_BUILD_EXAMPLES)\n  add_subdirectory(tests)\nendif()\n"
  },
  {
    "path": "muduo/base/Condition.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Condition.h\"\n\n#include <errno.h>\n\n// returns true if time out, false otherwise.\nbool muduo::Condition::waitForSeconds(double seconds)\n{\n  struct timespec abstime;\n  // FIXME: use CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW to prevent time rewind.\n  clock_gettime(CLOCK_REALTIME, &abstime);\n\n  const int64_t kNanoSecondsPerSecond = 1000000000;\n  int64_t nanoseconds = static_cast<int64_t>(seconds * kNanoSecondsPerSecond);\n\n  abstime.tv_sec += static_cast<time_t>((abstime.tv_nsec + nanoseconds) / kNanoSecondsPerSecond);\n  abstime.tv_nsec = static_cast<long>((abstime.tv_nsec + nanoseconds) % kNanoSecondsPerSecond);\n\n  MutexLock::UnassignGuard ug(mutex_);\n  return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime);\n}\n\n"
  },
  {
    "path": "muduo/base/Condition.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_CONDITION_H\n#define MUDUO_BASE_CONDITION_H\n\n#include \"muduo/base/Mutex.h\"\n\n#include <pthread.h>\n\nnamespace muduo\n{\n\nclass Condition : noncopyable\n{\n public:\n  explicit Condition(MutexLock& mutex)\n    : mutex_(mutex)\n  {\n    MCHECK(pthread_cond_init(&pcond_, NULL));\n  }\n\n  ~Condition()\n  {\n    MCHECK(pthread_cond_destroy(&pcond_));\n  }\n\n  void wait()\n  {\n    MutexLock::UnassignGuard ug(mutex_);\n    MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));\n  }\n\n  // returns true if time out, false otherwise.\n  bool waitForSeconds(double seconds);\n\n  void notify()\n  {\n    MCHECK(pthread_cond_signal(&pcond_));\n  }\n\n  void notifyAll()\n  {\n    MCHECK(pthread_cond_broadcast(&pcond_));\n  }\n\n private:\n  MutexLock& mutex_;\n  pthread_cond_t pcond_;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_CONDITION_H\n"
  },
  {
    "path": "muduo/base/CountDownLatch.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/CountDownLatch.h\"\n\nusing namespace muduo;\n\nCountDownLatch::CountDownLatch(int count)\n  : mutex_(),\n    condition_(mutex_),\n    count_(count)\n{\n}\n\nvoid CountDownLatch::wait()\n{\n  MutexLockGuard lock(mutex_);\n  while (count_ > 0)\n  {\n    condition_.wait();\n  }\n}\n\nvoid CountDownLatch::countDown()\n{\n  MutexLockGuard lock(mutex_);\n  --count_;\n  if (count_ == 0)\n  {\n    condition_.notifyAll();\n  }\n}\n\nint CountDownLatch::getCount() const\n{\n  MutexLockGuard lock(mutex_);\n  return count_;\n}\n\n"
  },
  {
    "path": "muduo/base/CountDownLatch.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_COUNTDOWNLATCH_H\n#define MUDUO_BASE_COUNTDOWNLATCH_H\n\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/Mutex.h\"\n\nnamespace muduo\n{\n\nclass CountDownLatch : noncopyable\n{\n public:\n\n  explicit CountDownLatch(int count);\n\n  void wait();\n\n  void countDown();\n\n  int getCount() const;\n\n private:\n  mutable MutexLock mutex_;\n  Condition condition_ GUARDED_BY(mutex_);\n  int count_ GUARDED_BY(mutex_);\n};\n\n}  // namespace muduo\n#endif  // MUDUO_BASE_COUNTDOWNLATCH_H\n"
  },
  {
    "path": "muduo/base/CurrentThread.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/CurrentThread.h\"\n\n#include <cxxabi.h>\n#include <execinfo.h>\n#include <stdlib.h>\n\nnamespace muduo\n{\nnamespace CurrentThread\n{\n__thread int t_cachedTid = 0;\n__thread char t_tidString[32];\n__thread int t_tidStringLength = 6;\n__thread const char* t_threadName = \"unknown\";\nstatic_assert(std::is_same<int, pid_t>::value, \"pid_t should be int\");\n\nstring stackTrace(bool demangle)\n{\n  string stack;\n  const int max_frames = 200;\n  void* frame[max_frames];\n  int nptrs = ::backtrace(frame, max_frames);\n  char** strings = ::backtrace_symbols(frame, nptrs);\n  if (strings)\n  {\n    size_t len = 256;\n    char* demangled = demangle ? static_cast<char*>(::malloc(len)) : nullptr;\n    for (int i = 1; i < nptrs; ++i)  // skipping the 0-th, which is this function\n    {\n      if (demangle)\n      {\n        // https://panthema.net/2008/0901-stacktrace-demangled/\n        // bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909]\n        char* left_par = nullptr;\n        char* plus = nullptr;\n        for (char* p = strings[i]; *p; ++p)\n        {\n          if (*p == '(')\n            left_par = p;\n          else if (*p == '+')\n            plus = p;\n        }\n\n        if (left_par && plus)\n        {\n          *plus = '\\0';\n          int status = 0;\n          char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status);\n          *plus = '+';\n          if (status == 0)\n          {\n            demangled = ret;  // ret could be realloc()\n            stack.append(strings[i], left_par+1);\n            stack.append(demangled);\n            stack.append(plus);\n            stack.push_back('\\n');\n            continue;\n          }\n        }\n      }\n      // Fallback to mangled names\n      stack.append(strings[i]);\n      stack.push_back('\\n');\n    }\n    free(demangled);\n    free(strings);\n  }\n  return stack;\n}\n\n}  // namespace CurrentThread\n}  // namespace muduo\n"
  },
  {
    "path": "muduo/base/CurrentThread.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_CURRENTTHREAD_H\n#define MUDUO_BASE_CURRENTTHREAD_H\n\n#include \"muduo/base/Types.h\"\n\nnamespace muduo\n{\nnamespace CurrentThread\n{\n  // internal\n  extern __thread int t_cachedTid;\n  extern __thread char t_tidString[32];\n  extern __thread int t_tidStringLength;\n  extern __thread const char* t_threadName;\n  void cacheTid();\n\n  inline int tid()\n  {\n    if (__builtin_expect(t_cachedTid == 0, 0))\n    {\n      cacheTid();\n    }\n    return t_cachedTid;\n  }\n\n  inline const char* tidString() // for logging\n  {\n    return t_tidString;\n  }\n\n  inline int tidStringLength() // for logging\n  {\n    return t_tidStringLength;\n  }\n\n  inline const char* name()\n  {\n    return t_threadName;\n  }\n\n  bool isMainThread();\n\n  void sleepUsec(int64_t usec);  // for testing\n\n  string stackTrace(bool demangle);\n}  // namespace CurrentThread\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_CURRENTTHREAD_H\n"
  },
  {
    "path": "muduo/base/Date.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Date.h\"\n#include <stdio.h>  // snprintf\n#include <time.h>  // struct tm\n\nnamespace muduo\n{\nnamespace detail\n{\n\nchar require_32_bit_integer_at_least[sizeof(int) >= sizeof(int32_t) ? 1 : -1];\n\n// algorithm and explanation see:\n// http://www.faqs.org/faqs/calendars/faq/part2/\n// http://blog.csdn.net/Solstice\n\nint getJulianDayNumber(int year, int month, int day)\n{\n  (void) require_32_bit_integer_at_least; // no warning please\n  int a = (14 - month) / 12;\n  int y = year + 4800 - a;\n  int m = month + 12 * a - 3;\n  return day + (153*m + 2) / 5 + y*365 + y/4 - y/100 + y/400 - 32045;\n}\n\nstruct Date::YearMonthDay getYearMonthDay(int julianDayNumber)\n{\n  int a = julianDayNumber + 32044;\n  int b = (4 * a + 3) / 146097;\n  int c = a - ((b * 146097) / 4);\n  int d = (4 * c + 3) / 1461;\n  int e = c - ((1461 * d) / 4);\n  int m = (5 * e + 2) / 153;\n  Date::YearMonthDay ymd;\n  ymd.day = e - ((153 * m + 2) / 5) + 1;\n  ymd.month = m + 3 - 12 * (m / 10);\n  ymd.year = b * 100 + d - 4800 + (m / 10);\n  return ymd;\n}\n}  // namespace detail\nconst int Date::kJulianDayOf1970_01_01 = detail::getJulianDayNumber(1970, 1, 1);\n}  // namespace muduo\n\nusing namespace muduo;\nusing namespace muduo::detail;\n\nDate::Date(int y, int m, int d)\n  : julianDayNumber_(getJulianDayNumber(y, m, d))\n{\n}\n\nDate::Date(const struct tm& t)\n  : julianDayNumber_(getJulianDayNumber(\n        t.tm_year+1900,\n        t.tm_mon+1,\n        t.tm_mday))\n{\n}\n\nstring Date::toIsoString() const\n{\n  char buf[32];\n  YearMonthDay ymd(yearMonthDay());\n  snprintf(buf, sizeof buf, \"%4d-%02d-%02d\", ymd.year, ymd.month, ymd.day);\n  return buf;\n}\n\nDate::YearMonthDay Date::yearMonthDay() const\n{\n  return getYearMonthDay(julianDayNumber_);\n}\n\n"
  },
  {
    "path": "muduo/base/Date.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_DATE_H\n#define MUDUO_BASE_DATE_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/Types.h\"\n\nstruct tm;\n\nnamespace muduo\n{\n\n///\n/// Date in Gregorian calendar.\n///\n/// This class is immutable.\n/// It's recommended to pass it by value, since it's passed in register on x64.\n///\nclass Date : public muduo::copyable\n          // public boost::less_than_comparable<Date>,\n          // public boost::equality_comparable<Date>\n{\n public:\n\n  struct YearMonthDay\n  {\n    int year; // [1900..2500]\n    int month;  // [1..12]\n    int day;  // [1..31]\n  };\n\n  static const int kDaysPerWeek = 7;\n  static const int kJulianDayOf1970_01_01;\n\n  ///\n  /// Constucts an invalid Date.\n  ///\n  Date()\n    : julianDayNumber_(0)\n  {}\n\n  ///\n  /// Constucts a yyyy-mm-dd Date.\n  ///\n  /// 1 <= month <= 12\n  Date(int year, int month, int day);\n\n  ///\n  /// Constucts a Date from Julian Day Number.\n  ///\n  explicit Date(int julianDayNum)\n    : julianDayNumber_(julianDayNum)\n  {}\n\n  ///\n  /// Constucts a Date from struct tm\n  ///\n  explicit Date(const struct tm&);\n\n  // default copy/assignment/dtor are Okay\n\n  void swap(Date& that)\n  {\n    std::swap(julianDayNumber_, that.julianDayNumber_);\n  }\n\n  bool valid() const { return julianDayNumber_ > 0; }\n\n  ///\n  /// Converts to yyyy-mm-dd format.\n  ///\n  string toIsoString() const;\n\n  struct YearMonthDay yearMonthDay() const;\n\n  int year() const\n  {\n    return yearMonthDay().year;\n  }\n\n  int month() const\n  {\n    return yearMonthDay().month;\n  }\n\n  int day() const\n  {\n    return yearMonthDay().day;\n  }\n\n  // [0, 1, ..., 6] => [Sunday, Monday, ..., Saturday ]\n  int weekDay() const\n  {\n    return (julianDayNumber_+1) % kDaysPerWeek;\n  }\n\n  int julianDayNumber() const { return julianDayNumber_; }\n\n private:\n  int julianDayNumber_;\n};\n\ninline bool operator<(Date x, Date y)\n{\n  return x.julianDayNumber() < y.julianDayNumber();\n}\n\ninline bool operator==(Date x, Date y)\n{\n  return x.julianDayNumber() == y.julianDayNumber();\n}\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_DATE_H\n"
  },
  {
    "path": "muduo/base/Exception.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Exception.h\"\n#include \"muduo/base/CurrentThread.h\"\n\nnamespace muduo\n{\n\nException::Exception(string msg)\n  : message_(std::move(msg)),\n    stack_(CurrentThread::stackTrace(/*demangle=*/false))\n{\n}\n\n}  // namespace muduo\n"
  },
  {
    "path": "muduo/base/Exception.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_EXCEPTION_H\n#define MUDUO_BASE_EXCEPTION_H\n\n#include \"muduo/base/Types.h\"\n#include <exception>\n\nnamespace muduo\n{\n\nclass Exception : public std::exception\n{\n public:\n  Exception(string what);\n  ~Exception() noexcept override = default;\n\n  // default copy-ctor and operator= are okay.\n\n  const char* what() const noexcept override\n  {\n    return message_.c_str();\n  }\n\n  const char* stackTrace() const noexcept\n  {\n    return stack_.c_str();\n  }\n\n private:\n  string message_;\n  string stack_;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_EXCEPTION_H\n"
  },
  {
    "path": "muduo/base/FileUtil.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\nusing namespace muduo;\n\nFileUtil::AppendFile::AppendFile(StringArg filename)\n  : fp_(::fopen(filename.c_str(), \"ae\")),  // 'e' for O_CLOEXEC\n    writtenBytes_(0)\n{\n  assert(fp_);\n  ::setbuffer(fp_, buffer_, sizeof buffer_);\n  // posix_fadvise POSIX_FADV_DONTNEED ?\n}\n\nFileUtil::AppendFile::~AppendFile()\n{\n  ::fclose(fp_);\n}\n\nvoid FileUtil::AppendFile::append(const char* logline, const size_t len)\n{\n  size_t written = 0;\n\n  while (written != len)\n  {\n    size_t remain = len - written;\n    size_t n = write(logline + written, remain);\n    if (n != remain)\n    {\n      int err = ferror(fp_);\n      if (err)\n      {\n        fprintf(stderr, \"AppendFile::append() failed %s\\n\", strerror_tl(err));\n        break;\n      }\n    }\n    written += n;\n  }\n\n  writtenBytes_ += written;\n}\n\nvoid FileUtil::AppendFile::flush()\n{\n  ::fflush(fp_);\n}\n\nsize_t FileUtil::AppendFile::write(const char* logline, size_t len)\n{\n  // #undef fwrite_unlocked\n  return ::fwrite_unlocked(logline, 1, len, fp_);\n}\n\nFileUtil::ReadSmallFile::ReadSmallFile(StringArg filename)\n  : fd_(::open(filename.c_str(), O_RDONLY | O_CLOEXEC)),\n    err_(0)\n{\n  buf_[0] = '\\0';\n  if (fd_ < 0)\n  {\n    err_ = errno;\n  }\n}\n\nFileUtil::ReadSmallFile::~ReadSmallFile()\n{\n  if (fd_ >= 0)\n  {\n    ::close(fd_); // FIXME: check EINTR\n  }\n}\n\n// return errno\ntemplate<typename String>\nint FileUtil::ReadSmallFile::readToString(int maxSize,\n                                          String* content,\n                                          int64_t* fileSize,\n                                          int64_t* modifyTime,\n                                          int64_t* createTime)\n{\n  static_assert(sizeof(off_t) == 8, \"_FILE_OFFSET_BITS = 64\");\n  assert(content != NULL);\n  int err = err_;\n  if (fd_ >= 0)\n  {\n    content->clear();\n\n    if (fileSize)\n    {\n      struct stat statbuf;\n      if (::fstat(fd_, &statbuf) == 0)\n      {\n        if (S_ISREG(statbuf.st_mode))\n        {\n          *fileSize = statbuf.st_size;\n          content->reserve(static_cast<int>(std::min(implicit_cast<int64_t>(maxSize), *fileSize)));\n        }\n        else if (S_ISDIR(statbuf.st_mode))\n        {\n          err = EISDIR;\n        }\n        if (modifyTime)\n        {\n          *modifyTime = statbuf.st_mtime;\n        }\n        if (createTime)\n        {\n          *createTime = statbuf.st_ctime;\n        }\n      }\n      else\n      {\n        err = errno;\n      }\n    }\n\n    while (content->size() < implicit_cast<size_t>(maxSize))\n    {\n      size_t toRead = std::min(implicit_cast<size_t>(maxSize) - content->size(), sizeof(buf_));\n      ssize_t n = ::read(fd_, buf_, toRead);\n      if (n > 0)\n      {\n        content->append(buf_, n);\n      }\n      else\n      {\n        if (n < 0)\n        {\n          err = errno;\n        }\n        break;\n      }\n    }\n  }\n  return err;\n}\n\nint FileUtil::ReadSmallFile::readToBuffer(int* size)\n{\n  int err = err_;\n  if (fd_ >= 0)\n  {\n    ssize_t n = ::pread(fd_, buf_, sizeof(buf_)-1, 0);\n    if (n >= 0)\n    {\n      if (size)\n      {\n        *size = static_cast<int>(n);\n      }\n      buf_[n] = '\\0';\n    }\n    else\n    {\n      err = errno;\n    }\n  }\n  return err;\n}\n\ntemplate int FileUtil::readFile(StringArg filename,\n                                int maxSize,\n                                string* content,\n                                int64_t*, int64_t*, int64_t*);\n\ntemplate int FileUtil::ReadSmallFile::readToString(\n    int maxSize,\n    string* content,\n    int64_t*, int64_t*, int64_t*);\n\n"
  },
  {
    "path": "muduo/base/FileUtil.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_BASE_FILEUTIL_H\n#define MUDUO_BASE_FILEUTIL_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include <sys/types.h>  // for off_t\n\nnamespace muduo\n{\nnamespace FileUtil\n{\n\n// read small file < 64KB\nclass ReadSmallFile : noncopyable\n{\n public:\n  ReadSmallFile(StringArg filename);\n  ~ReadSmallFile();\n\n  // return errno\n  template<typename String>\n  int readToString(int maxSize,\n                   String* content,\n                   int64_t* fileSize,\n                   int64_t* modifyTime,\n                   int64_t* createTime);\n\n  /// Read at maxium kBufferSize into buf_\n  // return errno\n  int readToBuffer(int* size);\n\n  const char* buffer() const { return buf_; }\n\n  static const int kBufferSize = 64*1024;\n\n private:\n  int fd_;\n  int err_;\n  char buf_[kBufferSize];\n};\n\n// read the file content, returns errno if error happens.\ntemplate<typename String>\nint readFile(StringArg filename,\n             int maxSize,\n             String* content,\n             int64_t* fileSize = NULL,\n             int64_t* modifyTime = NULL,\n             int64_t* createTime = NULL)\n{\n  ReadSmallFile file(filename);\n  return file.readToString(maxSize, content, fileSize, modifyTime, createTime);\n}\n\n// not thread safe\nclass AppendFile : noncopyable\n{\n public:\n  explicit AppendFile(StringArg filename);\n\n  ~AppendFile();\n\n  void append(const char* logline, size_t len);\n\n  void flush();\n\n  off_t writtenBytes() const { return writtenBytes_; }\n\n private:\n\n  size_t write(const char* logline, size_t len);\n\n  FILE* fp_;\n  char buffer_[64*1024];\n  off_t writtenBytes_;\n};\n\n}  // namespace FileUtil\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_FILEUTIL_H\n\n"
  },
  {
    "path": "muduo/base/GzipFile.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#pragma once\n\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/noncopyable.h\"\n#include <zlib.h>\n\nnamespace muduo\n{\n\nclass GzipFile : noncopyable\n{\n public:\n  GzipFile(GzipFile&& rhs) noexcept\n    : file_(rhs.file_)\n  {\n    rhs.file_ = NULL;\n  }\n\n  ~GzipFile()\n  {\n    if (file_)\n    {\n      ::gzclose(file_);\n    }\n  }\n\n  GzipFile& operator=(GzipFile&& rhs) noexcept\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  bool valid() const { return file_ != NULL; }\n  void swap(GzipFile& rhs) { std::swap(file_, rhs.file_); }\n#if ZLIB_VERNUM >= 0x1240\n  bool setBuffer(int size) { return ::gzbuffer(file_, size) == 0; }\n#endif\n\n  // return the number of uncompressed bytes actually read, 0 for eof, -1 for error\n  int read(void* buf, int len) { return ::gzread(file_, buf, len); }\n\n  // return the number of uncompressed bytes actually written\n  int write(StringPiece buf) { return ::gzwrite(file_, buf.data(), buf.size()); }\n\n  // number of uncompressed bytes\n  off_t tell() const { return ::gztell(file_); }\n\n#if ZLIB_VERNUM >= 0x1240\n  // number of compressed bytes\n  off_t offset() const { return ::gzoffset(file_); }\n#endif\n\n  // int flush(int f) { return ::gzflush(file_, f); }\n\n  static GzipFile openForRead(StringArg filename)\n  {\n    return GzipFile(::gzopen(filename.c_str(), \"rbe\"));\n  }\n\n  static GzipFile openForAppend(StringArg filename)\n  {\n    return GzipFile(::gzopen(filename.c_str(), \"abe\"));\n  }\n\n  static GzipFile openForWriteExclusive(StringArg filename)\n  {\n    return GzipFile(::gzopen(filename.c_str(), \"wbxe\"));\n  }\n\n  static GzipFile openForWriteTruncate(StringArg filename)\n  {\n    return GzipFile(::gzopen(filename.c_str(), \"wbe\"));\n  }\n\n private:\n  explicit GzipFile(gzFile file)\n    : file_(file)\n  {\n  }\n\n  gzFile file_;\n};\n\n}  // namespace muduo\n"
  },
  {
    "path": "muduo/base/LogFile.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/LogFile.h\"\n\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/ProcessInfo.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <time.h>\n\nusing namespace muduo;\n\nLogFile::LogFile(const string& basename,\n                 off_t rollSize,\n                 bool threadSafe,\n                 int flushInterval,\n                 int checkEveryN)\n  : basename_(basename),\n    rollSize_(rollSize),\n    flushInterval_(flushInterval),\n    checkEveryN_(checkEveryN),\n    count_(0),\n    mutex_(threadSafe ? new MutexLock : NULL),\n    startOfPeriod_(0),\n    lastRoll_(0),\n    lastFlush_(0)\n{\n  assert(basename.find('/') == string::npos);\n  rollFile();\n}\n\nLogFile::~LogFile() = default;\n\nvoid LogFile::append(const char* logline, int len)\n{\n  if (mutex_)\n  {\n    MutexLockGuard lock(*mutex_);\n    append_unlocked(logline, len);\n  }\n  else\n  {\n    append_unlocked(logline, len);\n  }\n}\n\nvoid LogFile::flush()\n{\n  if (mutex_)\n  {\n    MutexLockGuard lock(*mutex_);\n    file_->flush();\n  }\n  else\n  {\n    file_->flush();\n  }\n}\n\nvoid LogFile::append_unlocked(const char* logline, int len)\n{\n  file_->append(logline, len);\n\n  if (file_->writtenBytes() > rollSize_)\n  {\n    rollFile();\n  }\n  else\n  {\n    ++count_;\n    if (count_ >= checkEveryN_)\n    {\n      count_ = 0;\n      time_t now = ::time(NULL);\n      time_t thisPeriod_ = now / kRollPerSeconds_ * kRollPerSeconds_;\n      if (thisPeriod_ != startOfPeriod_)\n      {\n        rollFile();\n      }\n      else if (now - lastFlush_ > flushInterval_)\n      {\n        lastFlush_ = now;\n        file_->flush();\n      }\n    }\n  }\n}\n\nbool LogFile::rollFile()\n{\n  time_t now = 0;\n  string filename = getLogFileName(basename_, &now);\n  time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;\n\n  if (now > lastRoll_)\n  {\n    lastRoll_ = now;\n    lastFlush_ = now;\n    startOfPeriod_ = start;\n    file_.reset(new FileUtil::AppendFile(filename));\n    return true;\n  }\n  return false;\n}\n\nstring LogFile::getLogFileName(const string& basename, time_t* now)\n{\n  string filename;\n  filename.reserve(basename.size() + 64);\n  filename = basename;\n\n  char timebuf[32];\n  struct tm tm;\n  *now = time(NULL);\n  gmtime_r(now, &tm); // FIXME: localtime_r ?\n  strftime(timebuf, sizeof timebuf, \".%Y%m%d-%H%M%S.\", &tm);\n  filename += timebuf;\n\n  filename += ProcessInfo::hostname();\n\n  char pidbuf[32];\n  snprintf(pidbuf, sizeof pidbuf, \".%d\", ProcessInfo::pid());\n  filename += pidbuf;\n\n  filename += \".log\";\n\n  return filename;\n}\n\n"
  },
  {
    "path": "muduo/base/LogFile.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_LOGFILE_H\n#define MUDUO_BASE_LOGFILE_H\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Types.h\"\n\n#include <memory>\n\nnamespace muduo\n{\n\nnamespace FileUtil\n{\nclass AppendFile;\n}\n\nclass LogFile : noncopyable\n{\n public:\n  LogFile(const string& basename,\n          off_t rollSize,\n          bool threadSafe = true,\n          int flushInterval = 3,\n          int checkEveryN = 1024);\n  ~LogFile();\n\n  void append(const char* logline, int len);\n  void flush();\n  bool rollFile();\n\n private:\n  void append_unlocked(const char* logline, int len);\n\n  static string getLogFileName(const string& basename, time_t* now);\n\n  const string basename_;\n  const off_t rollSize_;\n  const int flushInterval_;\n  const int checkEveryN_;\n\n  int count_;\n\n  std::unique_ptr<MutexLock> mutex_;\n  time_t startOfPeriod_;\n  time_t lastRoll_;\n  time_t lastFlush_;\n  std::unique_ptr<FileUtil::AppendFile> file_;\n\n  const static int kRollPerSeconds_ = 60*60*24;\n};\n\n}  // namespace muduo\n#endif  // MUDUO_BASE_LOGFILE_H\n"
  },
  {
    "path": "muduo/base/LogStream.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/LogStream.h\"\n\n#include <algorithm>\n#include <limits>\n#include <type_traits>\n#include <assert.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdio.h>\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <inttypes.h>\n\n\nusing namespace muduo;\nusing namespace muduo::detail;\n\n// TODO: better itoa.\n#if defined(__clang__)\n#pragma clang diagnostic ignored \"-Wtautological-compare\"\n#else\n#pragma GCC diagnostic ignored \"-Wtype-limits\"\n#endif\n\nnamespace muduo\n{\nnamespace detail\n{\n\nconst char digits[] = \"9876543210123456789\";\nconst char* zero = digits + 9;\nstatic_assert(sizeof(digits) == 20, \"wrong number of digits\");\n\nconst char digitsHex[] = \"0123456789ABCDEF\";\nstatic_assert(sizeof digitsHex == 17, \"wrong number of digitsHex\");\n\n// Efficient Integer to String Conversions, by Matthew Wilson.\ntemplate<typename T>\nsize_t convert(char buf[], T value)\n{\n  T i = value;\n  char* p = buf;\n\n  do\n  {\n    int lsd = static_cast<int>(i % 10);\n    i /= 10;\n    *p++ = zero[lsd];\n  } while (i != 0);\n\n  if (value < 0)\n  {\n    *p++ = '-';\n  }\n  *p = '\\0';\n  std::reverse(buf, p);\n\n  return p - buf;\n}\n\nsize_t convertHex(char buf[], uintptr_t value)\n{\n  uintptr_t i = value;\n  char* p = buf;\n\n  do\n  {\n    int lsd = static_cast<int>(i % 16);\n    i /= 16;\n    *p++ = digitsHex[lsd];\n  } while (i != 0);\n\n  *p = '\\0';\n  std::reverse(buf, p);\n\n  return p - buf;\n}\n\ntemplate class FixedBuffer<kSmallBuffer>;\ntemplate class FixedBuffer<kLargeBuffer>;\n\n}  // namespace detail\n\n/*\n Format a number with 5 characters, including SI units.\n [0,     999]\n [1.00k, 999k]\n [1.00M, 999M]\n [1.00G, 999G]\n [1.00T, 999T]\n [1.00P, 999P]\n [1.00E, inf)\n*/\nstd::string formatSI(int64_t s)\n{\n  double n = static_cast<double>(s);\n  char buf[64];\n  if (s < 1000)\n    snprintf(buf, sizeof(buf), \"%\" PRId64, s);\n  else if (s < 9995)\n    snprintf(buf, sizeof(buf), \"%.2fk\", n/1e3);\n  else if (s < 99950)\n    snprintf(buf, sizeof(buf), \"%.1fk\", n/1e3);\n  else if (s < 999500)\n    snprintf(buf, sizeof(buf), \"%.0fk\", n/1e3);\n  else if (s < 9995000)\n    snprintf(buf, sizeof(buf), \"%.2fM\", n/1e6);\n  else if (s < 99950000)\n    snprintf(buf, sizeof(buf), \"%.1fM\", n/1e6);\n  else if (s < 999500000)\n    snprintf(buf, sizeof(buf), \"%.0fM\", n/1e6);\n  else if (s < 9995000000)\n    snprintf(buf, sizeof(buf), \"%.2fG\", n/1e9);\n  else if (s < 99950000000)\n    snprintf(buf, sizeof(buf), \"%.1fG\", n/1e9);\n  else if (s < 999500000000)\n    snprintf(buf, sizeof(buf), \"%.0fG\", n/1e9);\n  else if (s < 9995000000000)\n    snprintf(buf, sizeof(buf), \"%.2fT\", n/1e12);\n  else if (s < 99950000000000)\n    snprintf(buf, sizeof(buf), \"%.1fT\", n/1e12);\n  else if (s < 999500000000000)\n    snprintf(buf, sizeof(buf), \"%.0fT\", n/1e12);\n  else if (s < 9995000000000000)\n    snprintf(buf, sizeof(buf), \"%.2fP\", n/1e15);\n  else if (s < 99950000000000000)\n    snprintf(buf, sizeof(buf), \"%.1fP\", n/1e15);\n  else if (s < 999500000000000000)\n    snprintf(buf, sizeof(buf), \"%.0fP\", n/1e15);\n  else\n    snprintf(buf, sizeof(buf), \"%.2fE\", n/1e18);\n  return buf;\n}\n\n/*\n [0, 1023]\n [1.00Ki, 9.99Ki]\n [10.0Ki, 99.9Ki]\n [ 100Ki, 1023Ki]\n [1.00Mi, 9.99Mi]\n*/\nstd::string formatIEC(int64_t s)\n{\n  double n = static_cast<double>(s);\n  char buf[64];\n  const double Ki = 1024.0;\n  const double Mi = Ki * 1024.0;\n  const double Gi = Mi * 1024.0;\n  const double Ti = Gi * 1024.0;\n  const double Pi = Ti * 1024.0;\n  const double Ei = Pi * 1024.0;\n\n  if (n < Ki)\n    snprintf(buf, sizeof buf, \"%\" PRId64, s);\n  else if (n < Ki*9.995)\n    snprintf(buf, sizeof buf, \"%.2fKi\", n / Ki);\n  else if (n < Ki*99.95)\n    snprintf(buf, sizeof buf, \"%.1fKi\", n / Ki);\n  else if (n < Ki*1023.5)\n    snprintf(buf, sizeof buf, \"%.0fKi\", n / Ki);\n\n  else if (n < Mi*9.995)\n    snprintf(buf, sizeof buf, \"%.2fMi\", n / Mi);\n  else if (n < Mi*99.95)\n    snprintf(buf, sizeof buf, \"%.1fMi\", n / Mi);\n  else if (n < Mi*1023.5)\n    snprintf(buf, sizeof buf, \"%.0fMi\", n / Mi);\n\n  else if (n < Gi*9.995)\n    snprintf(buf, sizeof buf, \"%.2fGi\", n / Gi);\n  else if (n < Gi*99.95)\n    snprintf(buf, sizeof buf, \"%.1fGi\", n / Gi);\n  else if (n < Gi*1023.5)\n    snprintf(buf, sizeof buf, \"%.0fGi\", n / Gi);\n\n  else if (n < Ti*9.995)\n    snprintf(buf, sizeof buf, \"%.2fTi\", n / Ti);\n  else if (n < Ti*99.95)\n    snprintf(buf, sizeof buf, \"%.1fTi\", n / Ti);\n  else if (n < Ti*1023.5)\n    snprintf(buf, sizeof buf, \"%.0fTi\", n / Ti);\n\n  else if (n < Pi*9.995)\n    snprintf(buf, sizeof buf, \"%.2fPi\", n / Pi);\n  else if (n < Pi*99.95)\n    snprintf(buf, sizeof buf, \"%.1fPi\", n / Pi);\n  else if (n < Pi*1023.5)\n    snprintf(buf, sizeof buf, \"%.0fPi\", n / Pi);\n\n  else if (n < Ei*9.995)\n    snprintf(buf, sizeof buf, \"%.2fEi\", n / Ei );\n  else\n    snprintf(buf, sizeof buf, \"%.1fEi\", n / Ei );\n  return buf;\n}\n\n}  // namespace muduo\n\ntemplate<int SIZE>\nconst char* FixedBuffer<SIZE>::debugString()\n{\n  *cur_ = '\\0';\n  return data_;\n}\n\ntemplate<int SIZE>\nvoid FixedBuffer<SIZE>::cookieStart()\n{\n}\n\ntemplate<int SIZE>\nvoid FixedBuffer<SIZE>::cookieEnd()\n{\n}\n\nvoid LogStream::staticCheck()\n{\n  static_assert(kMaxNumericSize - 10 > std::numeric_limits<double>::digits10,\n                \"kMaxNumericSize is large enough\");\n  static_assert(kMaxNumericSize - 10 > std::numeric_limits<long double>::digits10,\n                \"kMaxNumericSize is large enough\");\n  static_assert(kMaxNumericSize - 10 > std::numeric_limits<long>::digits10,\n                \"kMaxNumericSize is large enough\");\n  static_assert(kMaxNumericSize - 10 > std::numeric_limits<long long>::digits10,\n                \"kMaxNumericSize is large enough\");\n}\n\ntemplate<typename T>\nvoid LogStream::formatInteger(T v)\n{\n  if (buffer_.avail() >= kMaxNumericSize)\n  {\n    size_t len = convert(buffer_.current(), v);\n    buffer_.add(len);\n  }\n}\n\nLogStream& LogStream::operator<<(short v)\n{\n  *this << static_cast<int>(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(unsigned short v)\n{\n  *this << static_cast<unsigned int>(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(int v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(unsigned int v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(long v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(unsigned long v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(long long v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(unsigned long long v)\n{\n  formatInteger(v);\n  return *this;\n}\n\nLogStream& LogStream::operator<<(const void* p)\n{\n  uintptr_t v = reinterpret_cast<uintptr_t>(p);\n  if (buffer_.avail() >= kMaxNumericSize)\n  {\n    char* buf = buffer_.current();\n    buf[0] = '0';\n    buf[1] = 'x';\n    size_t len = convertHex(buf+2, v);\n    buffer_.add(len+2);\n  }\n  return *this;\n}\n\n// FIXME: replace this with Grisu3 by Florian Loitsch.\nLogStream& LogStream::operator<<(double v)\n{\n  if (buffer_.avail() >= kMaxNumericSize)\n  {\n    int len = snprintf(buffer_.current(), kMaxNumericSize, \"%.12g\", v);\n    buffer_.add(len);\n  }\n  return *this;\n}\n\ntemplate<typename T>\nFmt::Fmt(const char* fmt, T val)\n{\n  static_assert(std::is_arithmetic<T>::value == true, \"Must be arithmetic type\");\n\n  length_ = snprintf(buf_, sizeof buf_, fmt, val);\n  assert(static_cast<size_t>(length_) < sizeof buf_);\n}\n\n// Explicit instantiations\n\ntemplate Fmt::Fmt(const char* fmt, char);\n\ntemplate Fmt::Fmt(const char* fmt, short);\ntemplate Fmt::Fmt(const char* fmt, unsigned short);\ntemplate Fmt::Fmt(const char* fmt, int);\ntemplate Fmt::Fmt(const char* fmt, unsigned int);\ntemplate Fmt::Fmt(const char* fmt, long);\ntemplate Fmt::Fmt(const char* fmt, unsigned long);\ntemplate Fmt::Fmt(const char* fmt, long long);\ntemplate Fmt::Fmt(const char* fmt, unsigned long long);\n\ntemplate Fmt::Fmt(const char* fmt, float);\ntemplate Fmt::Fmt(const char* fmt, double);\n"
  },
  {
    "path": "muduo/base/LogStream.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_LOGSTREAM_H\n#define MUDUO_BASE_LOGSTREAM_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n#include <assert.h>\n#include <string.h> // memcpy\n\nnamespace muduo\n{\n\nnamespace detail\n{\n\nconst int kSmallBuffer = 4000;\nconst int kLargeBuffer = 4000*1000;\n\ntemplate<int SIZE>\nclass FixedBuffer : noncopyable\n{\n public:\n  FixedBuffer()\n    : cur_(data_)\n  {\n    setCookie(cookieStart);\n  }\n\n  ~FixedBuffer()\n  {\n    setCookie(cookieEnd);\n  }\n\n  void append(const char* /*restrict*/ buf, size_t len)\n  {\n    // FIXME: append partially\n    if (implicit_cast<size_t>(avail()) > len)\n    {\n      memcpy(cur_, buf, len);\n      cur_ += len;\n    }\n  }\n\n  const char* data() const { return data_; }\n  int length() const { return static_cast<int>(cur_ - data_); }\n\n  // write to data_ directly\n  char* current() { return cur_; }\n  int avail() const { return static_cast<int>(end() - cur_); }\n  void add(size_t len) { cur_ += len; }\n\n  void reset() { cur_ = data_; }\n  void bzero() { memZero(data_, sizeof data_); }\n\n  // for used by GDB\n  const char* debugString();\n  void setCookie(void (*cookie)()) { cookie_ = cookie; }\n  // for used by unit test\n  string toString() const { return string(data_, length()); }\n  StringPiece toStringPiece() const { return StringPiece(data_, length()); }\n\n private:\n  const char* end() const { return data_ + sizeof data_; }\n  // Must be outline function for cookies.\n  static void cookieStart();\n  static void cookieEnd();\n\n  void (*cookie_)();\n  char data_[SIZE];\n  char* cur_;\n};\n\n}  // namespace detail\n\nclass LogStream : noncopyable\n{\n  typedef LogStream self;\n public:\n  typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;\n\n  self& operator<<(bool v)\n  {\n    buffer_.append(v ? \"1\" : \"0\", 1);\n    return *this;\n  }\n\n  self& operator<<(short);\n  self& operator<<(unsigned short);\n  self& operator<<(int);\n  self& operator<<(unsigned int);\n  self& operator<<(long);\n  self& operator<<(unsigned long);\n  self& operator<<(long long);\n  self& operator<<(unsigned long long);\n\n  self& operator<<(const void*);\n\n  self& operator<<(float v)\n  {\n    *this << static_cast<double>(v);\n    return *this;\n  }\n  self& operator<<(double);\n  // self& operator<<(long double);\n\n  self& operator<<(char v)\n  {\n    buffer_.append(&v, 1);\n    return *this;\n  }\n\n  // self& operator<<(signed char);\n  // self& operator<<(unsigned char);\n\n  self& operator<<(const char* str)\n  {\n    if (str)\n    {\n      buffer_.append(str, strlen(str));\n    }\n    else\n    {\n      buffer_.append(\"(null)\", 6);\n    }\n    return *this;\n  }\n\n  self& operator<<(const unsigned char* str)\n  {\n    return operator<<(reinterpret_cast<const char*>(str));\n  }\n\n  self& operator<<(const string& v)\n  {\n    buffer_.append(v.c_str(), v.size());\n    return *this;\n  }\n\n  self& operator<<(const StringPiece& v)\n  {\n    buffer_.append(v.data(), v.size());\n    return *this;\n  }\n\n  self& operator<<(const Buffer& v)\n  {\n    *this << v.toStringPiece();\n    return *this;\n  }\n\n  void append(const char* data, int len) { buffer_.append(data, len); }\n  const Buffer& buffer() const { return buffer_; }\n  void resetBuffer() { buffer_.reset(); }\n\n private:\n  void staticCheck();\n\n  template<typename T>\n  void formatInteger(T);\n\n  Buffer buffer_;\n\n  static const int kMaxNumericSize = 48;\n};\n\nclass Fmt // : noncopyable\n{\n public:\n  template<typename T>\n  Fmt(const char* fmt, T val);\n\n  const char* data() const { return buf_; }\n  int length() const { return length_; }\n\n private:\n  char buf_[32];\n  int length_;\n};\n\ninline LogStream& operator<<(LogStream& s, const Fmt& fmt)\n{\n  s.append(fmt.data(), fmt.length());\n  return s;\n}\n\n// Format quantity n in SI units (k, M, G, T, P, E).\n// The returned string is atmost 5 characters long.\n// Requires n >= 0\nstring formatSI(int64_t n);\n\n// Format quantity n in IEC (binary) units (Ki, Mi, Gi, Ti, Pi, Ei).\n// The returned string is atmost 6 characters long.\n// Requires n >= 0\nstring formatIEC(int64_t n);\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_LOGSTREAM_H\n"
  },
  {
    "path": "muduo/base/Logging.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Logging.h\"\n\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/base/TimeZone.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <sstream>\n\nnamespace muduo\n{\n\n/*\nclass LoggerImpl\n{\n public:\n  typedef Logger::LogLevel LogLevel;\n  LoggerImpl(LogLevel level, int old_errno, const char* file, int line);\n  void finish();\n\n  Timestamp time_;\n  LogStream stream_;\n  LogLevel level_;\n  int line_;\n  const char* fullname_;\n  const char* basename_;\n};\n*/\n\n__thread char t_errnobuf[512];\n__thread char t_time[64];\n__thread time_t t_lastSecond;\n\nconst char* strerror_tl(int savedErrno)\n{\n  return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);\n}\n\nLogger::LogLevel initLogLevel()\n{\n  if (::getenv(\"MUDUO_LOG_TRACE\"))\n    return Logger::TRACE;\n  else if (::getenv(\"MUDUO_LOG_DEBUG\"))\n    return Logger::DEBUG;\n  else\n    return Logger::INFO;\n}\n\nLogger::LogLevel g_logLevel = initLogLevel();\n\nconst char* LogLevelName[Logger::NUM_LOG_LEVELS] =\n{\n  \"TRACE \",\n  \"DEBUG \",\n  \"INFO  \",\n  \"WARN  \",\n  \"ERROR \",\n  \"FATAL \",\n};\n\n// helper class for known string length at compile time\nclass T\n{\n public:\n  T(const char* str, unsigned len)\n    :str_(str),\n     len_(len)\n  {\n    assert(strlen(str) == len_);\n  }\n\n  const char* str_;\n  const unsigned len_;\n};\n\ninline LogStream& operator<<(LogStream& s, T v)\n{\n  s.append(v.str_, v.len_);\n  return s;\n}\n\ninline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)\n{\n  s.append(v.data_, v.size_);\n  return s;\n}\n\nvoid defaultOutput(const char* msg, int len)\n{\n  size_t n = fwrite(msg, 1, len, stdout);\n  //FIXME check n\n  (void)n;\n}\n\nvoid defaultFlush()\n{\n  fflush(stdout);\n}\n\nLogger::OutputFunc g_output = defaultOutput;\nLogger::FlushFunc g_flush = defaultFlush;\nTimeZone g_logTimeZone;\n\n}  // namespace muduo\n\nusing namespace muduo;\n\nLogger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)\n  : time_(Timestamp::now()),\n    stream_(),\n    level_(level),\n    line_(line),\n    basename_(file)\n{\n  formatTime();\n  CurrentThread::tid();\n  stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());\n  stream_ << T(LogLevelName[level], 6);\n  if (savedErrno != 0)\n  {\n    stream_ << strerror_tl(savedErrno) << \" (errno=\" << savedErrno << \") \";\n  }\n}\n\nvoid Logger::Impl::formatTime()\n{\n  int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();\n  time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond);\n  int microseconds = static_cast<int>(microSecondsSinceEpoch % Timestamp::kMicroSecondsPerSecond);\n  if (seconds != t_lastSecond)\n  {\n    t_lastSecond = seconds;\n    struct DateTime dt;\n    if (g_logTimeZone.valid())\n    {\n      dt = g_logTimeZone.toLocalTime(seconds);\n    }\n    else\n    {\n      dt = TimeZone::toUtcTime(seconds);\n    }\n\n    int len = snprintf(t_time, sizeof(t_time), \"%4d%02d%02d %02d:%02d:%02d\",\n        dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);\n    assert(len == 17); (void)len;\n  }\n\n  if (g_logTimeZone.valid())\n  {\n    Fmt us(\".%06d \", microseconds);\n    assert(us.length() == 8);\n    stream_ << T(t_time, 17) << T(us.data(), 8);\n  }\n  else\n  {\n    Fmt us(\".%06dZ \", microseconds);\n    assert(us.length() == 9);\n    stream_ << T(t_time, 17) << T(us.data(), 9);\n  }\n}\n\nvoid Logger::Impl::finish()\n{\n  stream_ << \" - \" << basename_ << ':' << line_ << '\\n';\n}\n\nLogger::Logger(SourceFile file, int line)\n  : impl_(INFO, 0, file, line)\n{\n}\n\nLogger::Logger(SourceFile file, int line, LogLevel level, const char* func)\n  : impl_(level, 0, file, line)\n{\n  impl_.stream_ << func << ' ';\n}\n\nLogger::Logger(SourceFile file, int line, LogLevel level)\n  : impl_(level, 0, file, line)\n{\n}\n\nLogger::Logger(SourceFile file, int line, bool toAbort)\n  : impl_(toAbort?FATAL:ERROR, errno, file, line)\n{\n}\n\nLogger::~Logger()\n{\n  impl_.finish();\n  const LogStream::Buffer& buf(stream().buffer());\n  g_output(buf.data(), buf.length());\n  if (impl_.level_ == FATAL)\n  {\n    g_flush();\n    abort();\n  }\n}\n\nvoid Logger::setLogLevel(Logger::LogLevel level)\n{\n  g_logLevel = level;\n}\n\nvoid Logger::setOutput(OutputFunc out)\n{\n  g_output = out;\n}\n\nvoid Logger::setFlush(FlushFunc flush)\n{\n  g_flush = flush;\n}\n\nvoid Logger::setTimeZone(const TimeZone& tz)\n{\n  g_logTimeZone = tz;\n}\n"
  },
  {
    "path": "muduo/base/Logging.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_LOGGING_H\n#define MUDUO_BASE_LOGGING_H\n\n#include \"muduo/base/LogStream.h\"\n#include \"muduo/base/Timestamp.h\"\n\nnamespace muduo\n{\n\nclass TimeZone;\n\nclass Logger\n{\n public:\n  enum LogLevel\n  {\n    TRACE,\n    DEBUG,\n    INFO,\n    WARN,\n    ERROR,\n    FATAL,\n    NUM_LOG_LEVELS,\n  };\n\n  // compile time calculation of basename of source file\n  class SourceFile\n  {\n   public:\n    template<int N>\n    SourceFile(const char (&arr)[N])\n      : data_(arr),\n        size_(N-1)\n    {\n      const char* slash = strrchr(data_, '/'); // builtin function\n      if (slash)\n      {\n        data_ = slash + 1;\n        size_ -= static_cast<int>(data_ - arr);\n      }\n    }\n\n    explicit SourceFile(const char* filename)\n      : data_(filename)\n    {\n      const char* slash = strrchr(filename, '/');\n      if (slash)\n      {\n        data_ = slash + 1;\n      }\n      size_ = static_cast<int>(strlen(data_));\n    }\n\n    const char* data_;\n    int size_;\n  };\n\n  Logger(SourceFile file, int line);\n  Logger(SourceFile file, int line, LogLevel level);\n  Logger(SourceFile file, int line, LogLevel level, const char* func);\n  Logger(SourceFile file, int line, bool toAbort);\n  ~Logger();\n\n  LogStream& stream() { return impl_.stream_; }\n\n  static LogLevel logLevel();\n  static void setLogLevel(LogLevel level);\n\n  typedef void (*OutputFunc)(const char* msg, int len);\n  typedef void (*FlushFunc)();\n  static void setOutput(OutputFunc);\n  static void setFlush(FlushFunc);\n  static void setTimeZone(const TimeZone& tz);\n\n private:\n\nclass Impl\n{\n public:\n  typedef Logger::LogLevel LogLevel;\n  Impl(LogLevel level, int old_errno, const SourceFile& file, int line);\n  void formatTime();\n  void finish();\n\n  Timestamp time_;\n  LogStream stream_;\n  LogLevel level_;\n  int line_;\n  SourceFile basename_;\n};\n\n  Impl impl_;\n\n};\n\nextern Logger::LogLevel g_logLevel;\n\ninline Logger::LogLevel Logger::logLevel()\n{\n  return g_logLevel;\n}\n\n//\n// CAUTION: do not write:\n//\n// if (good)\n//   LOG_INFO << \"Good news\";\n// else\n//   LOG_WARN << \"Bad news\";\n//\n// this expends to\n//\n// if (good)\n//   if (logging_INFO)\n//     logInfoStream << \"Good news\";\n//   else\n//     logWarnStream << \"Bad news\";\n//\n#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \\\n  muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()\n#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \\\n  muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()\n#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \\\n  muduo::Logger(__FILE__, __LINE__).stream()\n#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()\n#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()\n#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()\n#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()\n#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()\n\nconst char* strerror_tl(int savedErrno);\n\n// Taken from glog/logging.h\n//\n// Check that the input is non NULL.  This very useful in constructor\n// initializer lists.\n\n#define CHECK_NOTNULL(val) \\\n  ::muduo::CheckNotNull(__FILE__, __LINE__, \"'\" #val \"' Must be non NULL\", (val))\n\n// A small helper for CHECK_NOTNULL().\ntemplate <typename T>\nT* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr)\n{\n  if (ptr == NULL)\n  {\n   Logger(file, line, Logger::FATAL).stream() << names;\n  }\n  return ptr;\n}\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_LOGGING_H\n"
  },
  {
    "path": "muduo/base/Mutex.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_MUTEX_H\n#define MUDUO_BASE_MUTEX_H\n\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/noncopyable.h\"\n#include <assert.h>\n#include <pthread.h>\n\n// Thread safety annotations {\n// https://clang.llvm.org/docs/ThreadSafetyAnalysis.html\n\n// Enable thread safety attributes only with clang.\n// The attributes can be safely erased when compiling with other compilers.\n#if defined(__clang__) && (!defined(SWIG))\n#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))\n#else\n#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op\n#endif\n\n#define CAPABILITY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(capability(x))\n\n#define SCOPED_CAPABILITY \\\n  THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)\n\n#define GUARDED_BY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))\n\n#define PT_GUARDED_BY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))\n\n#define ACQUIRED_BEFORE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))\n\n#define ACQUIRED_AFTER(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))\n\n#define REQUIRES(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))\n\n#define REQUIRES_SHARED(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))\n\n#define ACQUIRE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))\n\n#define ACQUIRE_SHARED(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))\n\n#define RELEASE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))\n\n#define RELEASE_SHARED(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))\n\n#define TRY_ACQUIRE(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))\n\n#define TRY_ACQUIRE_SHARED(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))\n\n#define EXCLUDES(...) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))\n\n#define ASSERT_CAPABILITY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))\n\n#define ASSERT_SHARED_CAPABILITY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))\n\n#define RETURN_CAPABILITY(x) \\\n  THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))\n\n#define NO_THREAD_SAFETY_ANALYSIS \\\n  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)\n\n// End of thread safety annotations }\n\n#ifdef CHECK_PTHREAD_RETURN_VALUE\n\n#ifdef NDEBUG\n__BEGIN_DECLS\nextern void __assert_perror_fail (int errnum,\n                                  const char *file,\n                                  unsigned int line,\n                                  const char *function)\n    noexcept __attribute__ ((__noreturn__));\n__END_DECLS\n#endif\n\n#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \\\n                       if (__builtin_expect(errnum != 0, 0))    \\\n                         __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);})\n\n#else  // CHECK_PTHREAD_RETURN_VALUE\n\n#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \\\n                       assert(errnum == 0); (void) errnum;})\n\n#endif // CHECK_PTHREAD_RETURN_VALUE\n\nnamespace muduo\n{\n\n// Use as data member of a class, eg.\n//\n// class Foo\n// {\n//  public:\n//   int size() const;\n//\n//  private:\n//   mutable MutexLock mutex_;\n//   std::vector<int> data_ GUARDED_BY(mutex_);\n// };\nclass CAPABILITY(\"mutex\") MutexLock : noncopyable\n{\n public:\n  MutexLock()\n    : holder_(0)\n  {\n    MCHECK(pthread_mutex_init(&mutex_, NULL));\n  }\n\n  ~MutexLock()\n  {\n    assert(holder_ == 0);\n    MCHECK(pthread_mutex_destroy(&mutex_));\n  }\n\n  // must be called when locked, i.e. for assertion\n  bool isLockedByThisThread() const\n  {\n    return holder_ == CurrentThread::tid();\n  }\n\n  void assertLocked() const ASSERT_CAPABILITY(this)\n  {\n    assert(isLockedByThisThread());\n  }\n\n  // internal usage\n\n  void lock() ACQUIRE()\n  {\n    MCHECK(pthread_mutex_lock(&mutex_));\n    assignHolder();\n  }\n\n  void unlock() RELEASE()\n  {\n    unassignHolder();\n    MCHECK(pthread_mutex_unlock(&mutex_));\n  }\n\n  pthread_mutex_t* getPthreadMutex() /* non-const */\n  {\n    return &mutex_;\n  }\n\n private:\n  friend class Condition;\n\n  class UnassignGuard : noncopyable\n  {\n   public:\n    explicit UnassignGuard(MutexLock& owner)\n      : owner_(owner)\n    {\n      owner_.unassignHolder();\n    }\n\n    ~UnassignGuard()\n    {\n      owner_.assignHolder();\n    }\n\n   private:\n    MutexLock& owner_;\n  };\n\n  void unassignHolder()\n  {\n    holder_ = 0;\n  }\n\n  void assignHolder()\n  {\n    holder_ = CurrentThread::tid();\n  }\n\n  pthread_mutex_t mutex_;\n  pid_t holder_;\n};\n\n// Use as a stack variable, eg.\n// int Foo::size() const\n// {\n//   MutexLockGuard lock(mutex_);\n//   return data_.size();\n// }\nclass SCOPED_CAPABILITY MutexLockGuard : noncopyable\n{\n public:\n  explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)\n    : mutex_(mutex)\n  {\n    mutex_.lock();\n  }\n\n  ~MutexLockGuard() RELEASE()\n  {\n    mutex_.unlock();\n  }\n\n private:\n\n  MutexLock& mutex_;\n};\n\n}  // namespace muduo\n\n// Prevent misuse like:\n// MutexLockGuard(mutex_);\n// A tempory object doesn't hold the lock for long!\n#define MutexLockGuard(x) error \"Missing guard object name\"\n\n#endif  // MUDUO_BASE_MUTEX_H\n"
  },
  {
    "path": "muduo/base/ProcessInfo.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/ProcessInfo.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/FileUtil.h\"\n\n#include <algorithm>\n\n#include <assert.h>\n#include <dirent.h>\n#include <pwd.h>\n#include <stdio.h> // snprintf\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/resource.h>\n#include <sys/times.h>\n\nnamespace muduo\n{\nnamespace detail\n{\n__thread int t_numOpenedFiles = 0;\nint fdDirFilter(const struct dirent* d)\n{\n  if (::isdigit(d->d_name[0]))\n  {\n    ++t_numOpenedFiles;\n  }\n  return 0;\n}\n\n__thread std::vector<pid_t>* t_pids = NULL;\nint taskDirFilter(const struct dirent* d)\n{\n  if (::isdigit(d->d_name[0]))\n  {\n    t_pids->push_back(atoi(d->d_name));\n  }\n  return 0;\n}\n\nint scanDir(const char *dirpath, int (*filter)(const struct dirent *))\n{\n  struct dirent** namelist = NULL;\n  int result = ::scandir(dirpath, &namelist, filter, alphasort);\n  assert(namelist == NULL);\n  return result;\n}\n\nTimestamp g_startTime = Timestamp::now();\n// assume those won't change during the life time of a process.\nint g_clockTicks = static_cast<int>(::sysconf(_SC_CLK_TCK));\nint g_pageSize = static_cast<int>(::sysconf(_SC_PAGE_SIZE));\n}  // namespace detail\n}  // namespace muduo\n\nusing namespace muduo;\nusing namespace muduo::detail;\n\npid_t ProcessInfo::pid()\n{\n  return ::getpid();\n}\n\nstring ProcessInfo::pidString()\n{\n  char buf[32];\n  snprintf(buf, sizeof buf, \"%d\", pid());\n  return buf;\n}\n\nuid_t ProcessInfo::uid()\n{\n  return ::getuid();\n}\n\nstring ProcessInfo::username()\n{\n  struct passwd pwd;\n  struct passwd* result = NULL;\n  char buf[8192];\n  const char* name = \"unknownuser\";\n\n  getpwuid_r(uid(), &pwd, buf, sizeof buf, &result);\n  if (result)\n  {\n    name = pwd.pw_name;\n  }\n  return name;\n}\n\nuid_t ProcessInfo::euid()\n{\n  return ::geteuid();\n}\n\nTimestamp ProcessInfo::startTime()\n{\n  return g_startTime;\n}\n\nint ProcessInfo::clockTicksPerSecond()\n{\n  return g_clockTicks;\n}\n\nint ProcessInfo::pageSize()\n{\n  return g_pageSize;\n}\n\nbool ProcessInfo::isDebugBuild()\n{\n#ifdef NDEBUG\n  return false;\n#else\n  return true;\n#endif\n}\n\nstring ProcessInfo::hostname()\n{\n  // HOST_NAME_MAX 64\n  // _POSIX_HOST_NAME_MAX 255\n  char buf[256];\n  if (::gethostname(buf, sizeof buf) == 0)\n  {\n    buf[sizeof(buf)-1] = '\\0';\n    return buf;\n  }\n  else\n  {\n    return \"unknownhost\";\n  }\n}\n\nstring ProcessInfo::procname()\n{\n  return procname(procStat()).as_string();\n}\n\nStringPiece ProcessInfo::procname(const string& stat)\n{\n  StringPiece name;\n  size_t lp = stat.find('(');\n  size_t rp = stat.rfind(')');\n  if (lp != string::npos && rp != string::npos && lp < rp)\n  {\n    name.set(stat.data()+lp+1, static_cast<int>(rp-lp-1));\n  }\n  return name;\n}\n\nstring ProcessInfo::procStatus()\n{\n  string result;\n  FileUtil::readFile(\"/proc/self/status\", 65536, &result);\n  return result;\n}\n\nstring ProcessInfo::procStat()\n{\n  string result;\n  FileUtil::readFile(\"/proc/self/stat\", 65536, &result);\n  return result;\n}\n\nstring ProcessInfo::threadStat()\n{\n  char buf[64];\n  snprintf(buf, sizeof buf, \"/proc/self/task/%d/stat\", CurrentThread::tid());\n  string result;\n  FileUtil::readFile(buf, 65536, &result);\n  return result;\n}\n\nstring ProcessInfo::exePath()\n{\n  string result;\n  char buf[1024];\n  ssize_t n = ::readlink(\"/proc/self/exe\", buf, sizeof buf);\n  if (n > 0)\n  {\n    result.assign(buf, n);\n  }\n  return result;\n}\n\nint ProcessInfo::openedFiles()\n{\n  t_numOpenedFiles = 0;\n  scanDir(\"/proc/self/fd\", fdDirFilter);\n  return t_numOpenedFiles;\n}\n\nint ProcessInfo::maxOpenFiles()\n{\n  struct rlimit rl;\n  if (::getrlimit(RLIMIT_NOFILE, &rl))\n  {\n    return openedFiles();\n  }\n  else\n  {\n    return static_cast<int>(rl.rlim_cur);\n  }\n}\n\nProcessInfo::CpuTime ProcessInfo::cpuTime()\n{\n  ProcessInfo::CpuTime t;\n  struct tms tms;\n  if (::times(&tms) >= 0)\n  {\n    const double hz = static_cast<double>(clockTicksPerSecond());\n    t.userSeconds = static_cast<double>(tms.tms_utime) / hz;\n    t.systemSeconds = static_cast<double>(tms.tms_stime) / hz;\n  }\n  return t;\n}\n\nint ProcessInfo::numThreads()\n{\n  int result = 0;\n  string status = procStatus();\n  size_t pos = status.find(\"Threads:\");\n  if (pos != string::npos)\n  {\n    result = ::atoi(status.c_str() + pos + 8);\n  }\n  return result;\n}\n\nstd::vector<pid_t> ProcessInfo::threads()\n{\n  std::vector<pid_t> result;\n  t_pids = &result;\n  scanDir(\"/proc/self/task\", taskDirFilter);\n  t_pids = NULL;\n  std::sort(result.begin(), result.end());\n  return result;\n}\n\n"
  },
  {
    "path": "muduo/base/ProcessInfo.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_BASE_PROCESSINFO_H\n#define MUDUO_BASE_PROCESSINFO_H\n\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/base/Timestamp.h\"\n#include <vector>\n#include <sys/types.h>\n\nnamespace muduo\n{\n\nnamespace ProcessInfo\n{\n  pid_t pid();\n  string pidString();\n  uid_t uid();\n  string username();\n  uid_t euid();\n  Timestamp startTime();\n  int clockTicksPerSecond();\n  int pageSize();\n  bool isDebugBuild();  // constexpr\n\n  string hostname();\n  string procname();\n  StringPiece procname(const string& stat);\n\n  /// read /proc/self/status\n  string procStatus();\n\n  /// read /proc/self/stat\n  string procStat();\n\n  /// read /proc/self/task/tid/stat\n  string threadStat();\n\n  /// readlink /proc/self/exe\n  string exePath();\n\n  int openedFiles();\n  int maxOpenFiles();\n\n  struct CpuTime\n  {\n    double userSeconds;\n    double systemSeconds;\n\n    CpuTime() : userSeconds(0.0), systemSeconds(0.0) { }\n\n    double total() const { return userSeconds + systemSeconds; }\n  };\n  CpuTime cpuTime();\n\n  int numThreads();\n  std::vector<pid_t> threads();\n}  // namespace ProcessInfo\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_PROCESSINFO_H\n"
  },
  {
    "path": "muduo/base/Singleton.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_SINGLETON_H\n#define MUDUO_BASE_SINGLETON_H\n\n#include \"muduo/base/noncopyable.h\"\n\n#include <assert.h>\n#include <pthread.h>\n#include <stdlib.h> // atexit\n\nnamespace muduo\n{\n\nnamespace detail\n{\n// This doesn't detect inherited member functions!\n// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions\ntemplate<typename T>\nstruct has_no_destroy\n{\n  template <typename C> static char test(decltype(&C::no_destroy));\n  template <typename C> static int32_t test(...);\n  const static bool value = sizeof(test<T>(0)) == 1;\n};\n}  // namespace detail\n\ntemplate<typename T>\nclass Singleton : noncopyable\n{\n public:\n  Singleton() = delete;\n  ~Singleton() = delete;\n\n  static T& instance()\n  {\n    pthread_once(&ponce_, &Singleton::init);\n    assert(value_ != NULL);\n    return *value_;\n  }\n\n private:\n  static void init()\n  {\n    value_ = new T();\n    if (!detail::has_no_destroy<T>::value)\n    {\n      ::atexit(destroy);\n    }\n  }\n\n  static void destroy()\n  {\n    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];\n    T_must_be_complete_type dummy; (void) dummy;\n\n    delete value_;\n    value_ = NULL;\n  }\n\n private:\n  static pthread_once_t ponce_;\n  static T*             value_;\n};\n\ntemplate<typename T>\npthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;\n\ntemplate<typename T>\nT* Singleton<T>::value_ = NULL;\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_SINGLETON_H\n"
  },
  {
    "path": "muduo/base/StringPiece.h",
    "content": "// Taken from PCRE pcre_stringpiece.h\n//\n// Copyright (c) 2005, Google Inc.\n// All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//     * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n//\n// Author: Sanjay Ghemawat\n//\n// A string like object that points into another piece of memory.\n// Useful for providing an interface that allows clients to easily\n// pass in either a \"const char*\" or a \"string\".\n//\n// Arghh!  I wish C++ literals were automatically of type \"string\".\n\n#ifndef MUDUO_BASE_STRINGPIECE_H\n#define MUDUO_BASE_STRINGPIECE_H\n\n#include <string.h>\n#include <iosfwd>    // for ostream forward-declaration\n\n#include \"muduo/base/Types.h\"\n\nnamespace muduo\n{\n\n// For passing C-style string argument to a function.\nclass StringArg // copyable\n{\n public:\n  StringArg(const char* str)\n    : str_(str)\n  { }\n\n  StringArg(const string& str)\n    : str_(str.c_str())\n  { }\n\n  const char* c_str() const { return str_; }\n\n private:\n  const char* str_;\n};\n\nclass StringPiece {\n private:\n  const char*   ptr_;\n  int           length_;\n\n public:\n  // We provide non-explicit singleton constructors so users can pass\n  // in a \"const char*\" or a \"string\" wherever a \"StringPiece\" is\n  // expected.\n  StringPiece()\n    : ptr_(NULL), length_(0) { }\n  StringPiece(const char* str)\n    : ptr_(str), length_(static_cast<int>(strlen(ptr_))) { }\n  StringPiece(const unsigned char* str)\n    : ptr_(reinterpret_cast<const char*>(str)),\n      length_(static_cast<int>(strlen(ptr_))) { }\n  StringPiece(const string& str)\n    : ptr_(str.data()), length_(static_cast<int>(str.size())) { }\n  StringPiece(const char* offset, int len)\n    : ptr_(offset), length_(len) { }\n\n  // data() may return a pointer to a buffer with embedded NULs, and the\n  // returned buffer may or may not be null terminated.  Therefore it is\n  // typically a mistake to pass data() to a routine that expects a NUL\n  // terminated string.  Use \"as_string().c_str()\" if you really need to do\n  // this.  Or better yet, change your routine so it does not rely on NUL\n  // termination.\n  const char* data() const { return ptr_; }\n  int size() const { return length_; }\n  bool empty() const { return length_ == 0; }\n  const char* begin() const { return ptr_; }\n  const char* end() const { return ptr_ + length_; }\n\n  void clear() { ptr_ = NULL; length_ = 0; }\n  void set(const char* buffer, int len) { ptr_ = buffer; length_ = len; }\n  void set(const char* str) {\n    ptr_ = str;\n    length_ = static_cast<int>(strlen(str));\n  }\n  void set(const void* buffer, int len) {\n    ptr_ = reinterpret_cast<const char*>(buffer);\n    length_ = len;\n  }\n\n  char operator[](int i) const { return ptr_[i]; }\n\n  void remove_prefix(int n) {\n    ptr_ += n;\n    length_ -= n;\n  }\n\n  void remove_suffix(int n) {\n    length_ -= n;\n  }\n\n  bool operator==(const StringPiece& x) const {\n    return ((length_ == x.length_) &&\n            (memcmp(ptr_, x.ptr_, length_) == 0));\n  }\n  bool operator!=(const StringPiece& x) const {\n    return !(*this == x);\n  }\n\n#define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp)                             \\\n  bool operator cmp (const StringPiece& x) const {                           \\\n    int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \\\n    return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_)));          \\\n  }\n  STRINGPIECE_BINARY_PREDICATE(<,  <);\n  STRINGPIECE_BINARY_PREDICATE(<=, <);\n  STRINGPIECE_BINARY_PREDICATE(>=, >);\n  STRINGPIECE_BINARY_PREDICATE(>,  >);\n#undef STRINGPIECE_BINARY_PREDICATE\n\n  int compare(const StringPiece& x) const {\n    int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_);\n    if (r == 0) {\n      if (length_ < x.length_) r = -1;\n      else if (length_ > x.length_) r = +1;\n    }\n    return r;\n  }\n\n  string as_string() const {\n    return string(data(), size());\n  }\n\n  void CopyToString(string* target) const {\n    target->assign(ptr_, length_);\n  }\n\n  // Does \"this\" start with \"x\"\n  bool starts_with(const StringPiece& x) const {\n    return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0));\n  }\n};\n\n}  // namespace muduo\n\n// ------------------------------------------------------------------\n// Functions used to create STL containers that use StringPiece\n//  Remember that a StringPiece's lifetime had better be less than\n//  that of the underlying string or char*.  If it is not, then you\n//  cannot safely store a StringPiece into an STL container\n// ------------------------------------------------------------------\n\n#ifdef HAVE_TYPE_TRAITS\n// This makes vector<StringPiece> really fast for some STL implementations\ntemplate<> struct __type_traits<muduo::StringPiece> {\n  typedef __true_type    has_trivial_default_constructor;\n  typedef __true_type    has_trivial_copy_constructor;\n  typedef __true_type    has_trivial_assignment_operator;\n  typedef __true_type    has_trivial_destructor;\n  typedef __true_type    is_POD_type;\n};\n#endif\n\n// allow StringPiece to be logged\nstd::ostream& operator<<(std::ostream& o, const muduo::StringPiece& piece);\n\n#endif  // MUDUO_BASE_STRINGPIECE_H\n"
  },
  {
    "path": "muduo/base/Thread.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Exception.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <type_traits>\n\n#include <errno.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <sys/prctl.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <linux/unistd.h>\n\nnamespace muduo\n{\nnamespace detail\n{\n\npid_t gettid()\n{\n  return static_cast<pid_t>(::syscall(SYS_gettid));\n}\n\nvoid afterFork()\n{\n  muduo::CurrentThread::t_cachedTid = 0;\n  muduo::CurrentThread::t_threadName = \"main\";\n  CurrentThread::tid();\n  // no need to call pthread_atfork(NULL, NULL, &afterFork);\n}\n\nclass ThreadNameInitializer\n{\n public:\n  ThreadNameInitializer()\n  {\n    muduo::CurrentThread::t_threadName = \"main\";\n    CurrentThread::tid();\n    pthread_atfork(NULL, NULL, &afterFork);\n  }\n};\n\nThreadNameInitializer init;\n\nstruct ThreadData\n{\n  typedef muduo::Thread::ThreadFunc ThreadFunc;\n  ThreadFunc func_;\n  string name_;\n  pid_t* tid_;\n  CountDownLatch* latch_;\n\n  ThreadData(ThreadFunc func,\n             const string& name,\n             pid_t* tid,\n             CountDownLatch* latch)\n    : func_(std::move(func)),\n      name_(name),\n      tid_(tid),\n      latch_(latch)\n  { }\n\n  void runInThread()\n  {\n    *tid_ = muduo::CurrentThread::tid();\n    tid_ = NULL;\n    latch_->countDown();\n    latch_ = NULL;\n\n    muduo::CurrentThread::t_threadName = name_.empty() ? \"muduoThread\" : name_.c_str();\n    ::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);\n    try\n    {\n      func_();\n      muduo::CurrentThread::t_threadName = \"finished\";\n    }\n    catch (const Exception& ex)\n    {\n      muduo::CurrentThread::t_threadName = \"crashed\";\n      fprintf(stderr, \"exception caught in Thread %s\\n\", name_.c_str());\n      fprintf(stderr, \"reason: %s\\n\", ex.what());\n      fprintf(stderr, \"stack trace: %s\\n\", ex.stackTrace());\n      abort();\n    }\n    catch (const std::exception& ex)\n    {\n      muduo::CurrentThread::t_threadName = \"crashed\";\n      fprintf(stderr, \"exception caught in Thread %s\\n\", name_.c_str());\n      fprintf(stderr, \"reason: %s\\n\", ex.what());\n      abort();\n    }\n    catch (...)\n    {\n      muduo::CurrentThread::t_threadName = \"crashed\";\n      fprintf(stderr, \"unknown exception caught in Thread %s\\n\", name_.c_str());\n      throw; // rethrow\n    }\n  }\n};\n\nvoid* startThread(void* obj)\n{\n  ThreadData* data = static_cast<ThreadData*>(obj);\n  data->runInThread();\n  delete data;\n  return NULL;\n}\n\n}  // namespace detail\n\nvoid CurrentThread::cacheTid()\n{\n  if (t_cachedTid == 0)\n  {\n    t_cachedTid = detail::gettid();\n    t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, \"%5d \", t_cachedTid);\n  }\n}\n\nbool CurrentThread::isMainThread()\n{\n  return tid() == ::getpid();\n}\n\nvoid CurrentThread::sleepUsec(int64_t usec)\n{\n  struct timespec ts = { 0, 0 };\n  ts.tv_sec = static_cast<time_t>(usec / Timestamp::kMicroSecondsPerSecond);\n  ts.tv_nsec = static_cast<long>(usec % Timestamp::kMicroSecondsPerSecond * 1000);\n  ::nanosleep(&ts, NULL);\n}\n\nAtomicInt32 Thread::numCreated_;\n\nThread::Thread(ThreadFunc func, const string& n)\n  : started_(false),\n    joined_(false),\n    pthreadId_(0),\n    tid_(0),\n    func_(std::move(func)),\n    name_(n),\n    latch_(1)\n{\n  setDefaultName();\n}\n\nThread::~Thread()\n{\n  if (started_ && !joined_)\n  {\n    pthread_detach(pthreadId_);\n  }\n}\n\nvoid Thread::setDefaultName()\n{\n  int num = numCreated_.incrementAndGet();\n  if (name_.empty())\n  {\n    char buf[32];\n    snprintf(buf, sizeof buf, \"Thread%d\", num);\n    name_ = buf;\n  }\n}\n\nvoid Thread::start()\n{\n  assert(!started_);\n  started_ = true;\n  // FIXME: move(func_)\n  detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);\n  if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))\n  {\n    started_ = false;\n    delete data; // or no delete?\n    LOG_SYSFATAL << \"Failed in pthread_create\";\n  }\n  else\n  {\n    latch_.wait();\n    assert(tid_ > 0);\n  }\n}\n\nint Thread::join()\n{\n  assert(started_);\n  assert(!joined_);\n  joined_ = true;\n  return pthread_join(pthreadId_, NULL);\n}\n\n}  // namespace muduo\n"
  },
  {
    "path": "muduo/base/Thread.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_THREAD_H\n#define MUDUO_BASE_THREAD_H\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Types.h\"\n\n#include <functional>\n#include <memory>\n#include <pthread.h>\n\nnamespace muduo\n{\n\nclass Thread : noncopyable\n{\n public:\n  typedef std::function<void ()> ThreadFunc;\n\n  explicit Thread(ThreadFunc, const string& name = string());\n  // FIXME: make it movable in C++11\n  ~Thread();\n\n  void start();\n  int join(); // return pthread_join()\n\n  bool started() const { return started_; }\n  // pthread_t pthreadId() const { return pthreadId_; }\n  pid_t tid() const { return tid_; }\n  const string& name() const { return name_; }\n\n  static int numCreated() { return numCreated_.get(); }\n\n private:\n  void setDefaultName();\n\n  bool       started_;\n  bool       joined_;\n  pthread_t  pthreadId_;\n  pid_t      tid_;\n  ThreadFunc func_;\n  string     name_;\n  CountDownLatch latch_;\n\n  static AtomicInt32 numCreated_;\n};\n\n}  // namespace muduo\n#endif  // MUDUO_BASE_THREAD_H\n"
  },
  {
    "path": "muduo/base/ThreadLocal.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_THREADLOCAL_H\n#define MUDUO_BASE_THREADLOCAL_H\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/noncopyable.h\"\n\n#include <pthread.h>\n\nnamespace muduo\n{\n\ntemplate<typename T>\nclass ThreadLocal : noncopyable\n{\n public:\n  ThreadLocal()\n  {\n    MCHECK(pthread_key_create(&pkey_, &ThreadLocal::destructor));\n  }\n\n  ~ThreadLocal()\n  {\n    MCHECK(pthread_key_delete(pkey_));\n  }\n\n  T& value()\n  {\n    T* perThreadValue = static_cast<T*>(pthread_getspecific(pkey_));\n    if (!perThreadValue)\n    {\n      T* newObj = new T();\n      MCHECK(pthread_setspecific(pkey_, newObj));\n      perThreadValue = newObj;\n    }\n    return *perThreadValue;\n  }\n\n private:\n\n  static void destructor(void *x)\n  {\n    T* obj = static_cast<T*>(x);\n    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];\n    T_must_be_complete_type dummy; (void) dummy;\n    delete obj;\n  }\n\n private:\n  pthread_key_t pkey_;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_THREADLOCAL_H\n"
  },
  {
    "path": "muduo/base/ThreadLocalSingleton.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_THREADLOCALSINGLETON_H\n#define MUDUO_BASE_THREADLOCALSINGLETON_H\n\n#include \"muduo/base/noncopyable.h\"\n\n#include <assert.h>\n#include <pthread.h>\n\nnamespace muduo\n{\n\ntemplate<typename T>\nclass ThreadLocalSingleton : noncopyable\n{\n public:\n  ThreadLocalSingleton() = delete;\n  ~ThreadLocalSingleton() = delete;\n\n  static T& instance()\n  {\n    if (!t_value_)\n    {\n      t_value_ = new T();\n      deleter_.set(t_value_);\n    }\n    return *t_value_;\n  }\n\n  static T* pointer()\n  {\n    return t_value_;\n  }\n\n private:\n  static void destructor(void* obj)\n  {\n    assert(obj == t_value_);\n    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];\n    T_must_be_complete_type dummy; (void) dummy;\n    delete t_value_;\n    t_value_ = 0;\n  }\n\n  class Deleter\n  {\n   public:\n    Deleter()\n    {\n      pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);\n    }\n\n    ~Deleter()\n    {\n      pthread_key_delete(pkey_);\n    }\n\n    void set(T* newObj)\n    {\n      assert(pthread_getspecific(pkey_) == NULL);\n      pthread_setspecific(pkey_, newObj);\n    }\n\n    pthread_key_t pkey_;\n  };\n\n  static __thread T* t_value_;\n  static Deleter deleter_;\n};\n\ntemplate<typename T>\n__thread T* ThreadLocalSingleton<T>::t_value_ = 0;\n\ntemplate<typename T>\ntypename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;\n\n}  // namespace muduo\n#endif  // MUDUO_BASE_THREADLOCALSINGLETON_H\n"
  },
  {
    "path": "muduo/base/ThreadPool.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/ThreadPool.h\"\n\n#include \"muduo/base/Exception.h\"\n\n#include <assert.h>\n#include <stdio.h>\n\nusing namespace muduo;\n\nThreadPool::ThreadPool(const string& nameArg)\n  : mutex_(),\n    notEmpty_(mutex_),\n    notFull_(mutex_),\n    name_(nameArg),\n    maxQueueSize_(0),\n    running_(false)\n{\n}\n\nThreadPool::~ThreadPool()\n{\n  if (running_)\n  {\n    stop();\n  }\n}\n\nvoid ThreadPool::start(int numThreads)\n{\n  assert(threads_.empty());\n  running_ = true;\n  threads_.reserve(numThreads);\n  for (int i = 0; i < numThreads; ++i)\n  {\n    char id[32];\n    snprintf(id, sizeof id, \"%d\", i+1);\n    threads_.emplace_back(new muduo::Thread(\n          std::bind(&ThreadPool::runInThread, this), name_+id));\n    threads_[i]->start();\n  }\n  if (numThreads == 0 && threadInitCallback_)\n  {\n    threadInitCallback_();\n  }\n}\n\nvoid ThreadPool::stop()\n{\n  {\n  MutexLockGuard lock(mutex_);\n  running_ = false;\n  notEmpty_.notifyAll();\n  notFull_.notifyAll();\n  }\n  for (auto& thr : threads_)\n  {\n    thr->join();\n  }\n}\n\nsize_t ThreadPool::queueSize() const\n{\n  MutexLockGuard lock(mutex_);\n  return queue_.size();\n}\n\nvoid ThreadPool::run(Task task)\n{\n  if (threads_.empty())\n  {\n    task();\n  }\n  else\n  {\n    MutexLockGuard lock(mutex_);\n    while (isFull() && running_)\n    {\n      notFull_.wait();\n    }\n    if (!running_) return;\n    assert(!isFull());\n\n    queue_.push_back(std::move(task));\n    notEmpty_.notify();\n  }\n}\n\nThreadPool::Task ThreadPool::take()\n{\n  MutexLockGuard lock(mutex_);\n  // always use a while-loop, due to spurious wakeup\n  while (queue_.empty() && running_)\n  {\n    notEmpty_.wait();\n  }\n  Task task;\n  if (!queue_.empty())\n  {\n    task = queue_.front();\n    queue_.pop_front();\n    if (maxQueueSize_ > 0)\n    {\n      notFull_.notify();\n    }\n  }\n  return task;\n}\n\nbool ThreadPool::isFull() const\n{\n  mutex_.assertLocked();\n  return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_;\n}\n\nvoid ThreadPool::runInThread()\n{\n  try\n  {\n    if (threadInitCallback_)\n    {\n      threadInitCallback_();\n    }\n    while (running_)\n    {\n      Task task(take());\n      if (task)\n      {\n        task();\n      }\n    }\n  }\n  catch (const Exception& ex)\n  {\n    fprintf(stderr, \"exception caught in ThreadPool %s\\n\", name_.c_str());\n    fprintf(stderr, \"reason: %s\\n\", ex.what());\n    fprintf(stderr, \"stack trace: %s\\n\", ex.stackTrace());\n    abort();\n  }\n  catch (const std::exception& ex)\n  {\n    fprintf(stderr, \"exception caught in ThreadPool %s\\n\", name_.c_str());\n    fprintf(stderr, \"reason: %s\\n\", ex.what());\n    abort();\n  }\n  catch (...)\n  {\n    fprintf(stderr, \"unknown exception caught in ThreadPool %s\\n\", name_.c_str());\n    throw; // rethrow\n  }\n}\n\n"
  },
  {
    "path": "muduo/base/ThreadPool.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_THREADPOOL_H\n#define MUDUO_BASE_THREADPOOL_H\n\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Types.h\"\n\n#include <deque>\n#include <vector>\n\nnamespace muduo\n{\n\nclass ThreadPool : noncopyable\n{\n public:\n  typedef std::function<void ()> Task;\n\n  explicit ThreadPool(const string& nameArg = string(\"ThreadPool\"));\n  ~ThreadPool();\n\n  // Must be called before start().\n  void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }\n  void setThreadInitCallback(const Task& cb)\n  { threadInitCallback_ = cb; }\n\n  void start(int numThreads);\n  void stop();\n\n  const string& name() const\n  { return name_; }\n\n  size_t queueSize() const;\n\n  // Could block if maxQueueSize > 0\n  // Call after stop() will return immediately.\n  // There is no move-only version of std::function in C++ as of C++14.\n  // So we don't need to overload a const& and an && versions\n  // as we do in (Bounded)BlockingQueue.\n  // https://stackoverflow.com/a/25408989\n  void run(Task f);\n\n private:\n  bool isFull() const REQUIRES(mutex_);\n  void runInThread();\n  Task take();\n\n  mutable MutexLock mutex_;\n  Condition notEmpty_ GUARDED_BY(mutex_);\n  Condition notFull_ GUARDED_BY(mutex_);\n  string name_;\n  Task threadInitCallback_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n  std::deque<Task> queue_ GUARDED_BY(mutex_);\n  size_t maxQueueSize_;\n  bool running_;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_THREADPOOL_H\n"
  },
  {
    "path": "muduo/base/TimeZone.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/TimeZone.h\"\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/Date.h\"\n\n#include <algorithm>\n#include <memory>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\n#include <assert.h>\n//#define _BSD_SOURCE\n#include <endian.h>\n\n#include <stdint.h>\n#include <stdio.h>\n\nusing namespace muduo;\n\nstruct TimeZone::Data\n{\n  struct Transition\n  {\n    int64_t utctime;\n    int64_t localtime;  // Shifted Epoch\n    int localtimeIdx;\n\n    Transition(int64_t t, int64_t l, int localIdx)\n        : utctime(t), localtime(l), localtimeIdx(localIdx)\n    { }\n  };\n\n  struct LocalTime\n  {\n    int32_t utcOffset;  // East of UTC\n    bool isDst;\n    int desigIdx;\n\n    LocalTime(int32_t offset, bool dst, int idx)\n        : utcOffset(offset), isDst(dst), desigIdx(idx)\n    { }\n  };\n\n  void addLocalTime(int32_t utcOffset, bool isDst, int desigIdx)\n  {\n    localtimes.push_back(LocalTime(utcOffset, isDst, desigIdx));\n  }\n\n  void addTransition(int64_t utcTime, int localtimeIdx)\n  {\n    LocalTime lt = localtimes.at(localtimeIdx);\n    transitions.push_back(Transition(utcTime, utcTime + lt.utcOffset, localtimeIdx));\n  }\n\n  const LocalTime* findLocalTime(int64_t utcTime) const;\n  const LocalTime* findLocalTime(const struct DateTime& local, bool postTransition) const;\n\n  struct CompareUtcTime\n  {\n    bool operator()(const Transition& lhs, const Transition& rhs) const\n    {\n      return lhs.utctime < rhs.utctime;\n    }\n  };\n\n  struct CompareLocalTime\n  {\n    bool operator()(const Transition& lhs, const Transition& rhs) const\n    {\n      return lhs.localtime < rhs.localtime;\n    }\n  };\n\n  std::vector<Transition> transitions;\n  std::vector<LocalTime> localtimes;\n  string abbreviation;\n  string tzstring;\n};\n\nnamespace muduo\n{\n\nconst int kSecondsPerDay = 24*60*60;\n\nnamespace detail\n{\n\nclass File : noncopyable\n{\n public:\n  File(const char* file)\n    : fp_(::fopen(file, \"rb\"))\n  {\n  }\n\n  ~File()\n  {\n    if (fp_)\n    {\n      ::fclose(fp_);\n    }\n  }\n\n  bool valid() const { return fp_; }\n\n  string readBytes(int n)\n  {\n    char buf[n];\n    ssize_t nr = ::fread(buf, 1, n, fp_);\n    if (nr != n)\n      throw std::logic_error(\"no enough data\");\n    return string(buf, n);\n  }\n\n  string readToEnd()\n  {\n    char buf[4096];\n    string result;\n    ssize_t nr = 0;\n    while ( (nr = ::fread(buf, 1, sizeof buf, fp_)) > 0)\n    {\n      result.append(buf, nr);\n    }\n    return result;\n  }\n\n  int64_t readInt64()\n  {\n    int64_t x = 0;\n    ssize_t nr = ::fread(&x, 1, sizeof(int64_t), fp_);\n    if (nr != sizeof(int64_t))\n      throw std::logic_error(\"bad int64_t data\");\n    return be64toh(x);\n  }\n\n  int32_t readInt32()\n  {\n    int32_t x = 0;\n    ssize_t nr = ::fread(&x, 1, sizeof(int32_t), fp_);\n    if (nr != sizeof(int32_t))\n      throw std::logic_error(\"bad int32_t data\");\n    return be32toh(x);\n  }\n\n  uint8_t readUInt8()\n  {\n    uint8_t x = 0;\n    ssize_t nr = ::fread(&x, 1, sizeof(uint8_t), fp_);\n    if (nr != sizeof(uint8_t))\n      throw std::logic_error(\"bad uint8_t data\");\n    return x;\n  }\n\n  off_t skip(ssize_t bytes)\n  {\n    return ::fseek(fp_, bytes, SEEK_CUR);\n  }\n\n private:\n  FILE* fp_;\n};\n\n// RFC 8536: https://www.rfc-editor.org/rfc/rfc8536.html\nbool readDataBlock(File& f, struct TimeZone::Data* data, bool v1)\n{\n  const int time_size = v1 ? sizeof(int32_t) : sizeof(int64_t);\n  const int32_t isutccnt = f.readInt32();\n  const int32_t isstdcnt = f.readInt32();\n  const int32_t leapcnt = f.readInt32();\n  const int32_t timecnt = f.readInt32();\n  const int32_t typecnt = f.readInt32();\n  const int32_t charcnt = f.readInt32();\n\n  if (leapcnt != 0)\n    return false;\n  if (isutccnt != 0 && isutccnt != typecnt)\n    return false;\n  if (isstdcnt != 0 && isstdcnt != typecnt)\n    return false;\n\n  std::vector<int64_t> trans;\n  trans.reserve(timecnt);\n  for (int i = 0; i < timecnt; ++i)\n  {\n    if (v1)\n    {\n      trans.push_back(f.readInt32());\n    }\n    else\n    {\n      trans.push_back(f.readInt64());\n    }\n  }\n\n  std::vector<int> localtimes;\n  localtimes.reserve(timecnt);\n  for (int i = 0; i < timecnt; ++i)\n  {\n    uint8_t local = f.readUInt8();\n    localtimes.push_back(local);\n  }\n\n  data->localtimes.reserve(typecnt);\n  for (int i = 0; i < typecnt; ++i)\n  {\n    int32_t gmtoff = f.readInt32();\n    uint8_t isdst = f.readUInt8();\n    uint8_t abbrind = f.readUInt8();\n\n    data->addLocalTime(gmtoff, isdst, abbrind);\n  }\n\n  for (int i = 0; i < timecnt; ++i)\n  {\n    int localIdx = localtimes[i];\n    data->addTransition(trans[i], localIdx);\n  }\n\n  data->abbreviation = f.readBytes(charcnt);\n  f.skip(leapcnt * (time_size + 4));\n  f.skip(isstdcnt);\n  f.skip(isutccnt);\n\n  if (!v1)\n  {\n    // FIXME: read to next new-line.\n    data->tzstring = f.readToEnd();\n  }\n\n  return true;\n}\n\nbool readTimeZoneFile(const char* zonefile, struct TimeZone::Data* data)\n{\n  File f(zonefile);\n  if (f.valid())\n  {\n    try\n    {\n      string head = f.readBytes(4);\n      if (head != \"TZif\")\n        throw std::logic_error(\"bad head\");\n      string version = f.readBytes(1);\n      f.readBytes(15);\n\n      const int32_t isgmtcnt = f.readInt32();\n      const int32_t isstdcnt = f.readInt32();\n      const int32_t leapcnt = f.readInt32();\n      const int32_t timecnt = f.readInt32();\n      const int32_t typecnt = f.readInt32();\n      const int32_t charcnt = f.readInt32();\n\n      if (version == \"2\")\n      {\n        size_t skip = sizeof(int32_t) * timecnt + timecnt + 6 * typecnt +\n            charcnt +  8 * leapcnt + isstdcnt + isgmtcnt;\n        f.skip(skip);\n\n        head = f.readBytes(4);\n        if (head != \"TZif\")\n          throw std::logic_error(\"bad head\");\n        f.skip(16);\n        return readDataBlock(f, data, false);\n      }\n      else\n      {\n        // TODO: Test with real v1 file.\n        f.skip(-4 * 6);  // Rewind to counters\n        return readDataBlock(f, data, true);\n      }\n    }\n    catch (std::logic_error& e)\n    {\n      fprintf(stderr, \"%s\\n\", e.what());\n    }\n  }\n  return false;\n}\n\ninline void fillHMS(unsigned seconds, struct DateTime* dt)\n{\n  dt->second = seconds % 60;\n  unsigned minutes = seconds / 60;\n  dt->minute = minutes % 60;\n  dt->hour = minutes / 60;\n}\n\nDateTime BreakTime(int64_t t)\n{\n  struct DateTime dt;\n  int seconds = static_cast<int>(t % kSecondsPerDay);\n  int days = static_cast<int>(t / kSecondsPerDay);\n  // C++11 rounds towards zero.\n  if (seconds < 0)\n  {\n    seconds += kSecondsPerDay;\n    --days;\n  }\n  detail::fillHMS(seconds, &dt);\n  Date date(days + Date::kJulianDayOf1970_01_01);\n  Date::YearMonthDay ymd = date.yearMonthDay();\n  dt.year = ymd.year;\n  dt.month = ymd.month;\n  dt.day = ymd.day;\n\n  return dt;\n}\n\n}  // namespace detail\n\n}  // namespace muduo\n\nconst TimeZone::Data::LocalTime* TimeZone::Data::findLocalTime(int64_t utcTime) const\n{\n  const LocalTime* local = NULL;\n\n  // row UTC time             isdst  offset  Local time (PRC)\n  //  1  1989-09-16 17:00:00Z   0      8.0   1989-09-17 01:00:00\n  //  2  1990-04-14 18:00:00Z   1      9.0   1990-04-15 03:00:00\n  //  3  1990-09-15 17:00:00Z   0      8.0   1990-09-16 01:00:00\n  //  4  1991-04-13 18:00:00Z   1      9.0   1991-04-14 03:00:00\n  //  5  1991-09-14 17:00:00Z   0      8.0   1991-09-15 01:00:00\n\n  // input '1990-06-01 00:00:00Z', std::upper_bound returns row 3,\n  // so the input is in range of row 2, offset is 9 hours,\n  // local time is 1990-06-01 09:00:00\n  if (transitions.empty() || utcTime < transitions.front().utctime)\n  {\n    // FIXME: should be first non dst time zone\n    local = &localtimes.front();\n  }\n  else\n  {\n    Transition sentry(utcTime, 0, 0);\n    std::vector<Transition>::const_iterator transI =\n        std::upper_bound(transitions.begin(), transitions.end(), sentry, CompareUtcTime());\n    assert(transI != transitions.begin());\n    if (transI != transitions.end())\n    {\n      --transI;\n      local = &localtimes[transI->localtimeIdx];\n    }\n    else\n    {\n      // FIXME: use TZ-env\n      local = &localtimes[transitions.back().localtimeIdx];\n    }\n  }\n\n  return local;\n}\n\nconst TimeZone::Data::LocalTime* TimeZone::Data::findLocalTime(\n    const struct DateTime& lt, bool postTransition) const\n{\n  const int64_t localtime = fromUtcTime(lt);\n\n  if (transitions.empty() || localtime < transitions.front().localtime)\n  {\n    // FIXME: should be first non dst time zone\n    return &localtimes.front();\n  }\n\n  Transition sentry(0, localtime, 0);\n  std::vector<Transition>::const_iterator transI =\n      std::upper_bound(transitions.begin(), transitions.end(), sentry, CompareLocalTime());\n  assert(transI != transitions.begin());\n\n  if (transI == transitions.end())\n  {\n    // FIXME: use TZ-env\n    return &localtimes[transitions.back().localtimeIdx];\n  }\n\n  Transition prior_trans = *(transI - 1);\n  int64_t prior_second = transI->utctime - 1 + localtimes[prior_trans.localtimeIdx].utcOffset;\n\n  // row UTC time             isdst  offset  Local time (PRC)     Prior second local time\n  //  1  1989-09-16 17:00:00Z   0      8.0   1989-09-17 01:00:00\n  //  2  1990-04-14 18:00:00Z   1      9.0   1990-04-15 03:00:00  1990-04-15 01:59:59\n  //  3  1990-09-15 17:00:00Z   0      8.0   1990-09-16 01:00:00  1990-09-16 01:59:59\n  //  4  1991-04-13 18:00:00Z   1      9.0   1991-04-14 03:00:00  1991-04-14 01:59:59\n  //  5  1991-09-14 17:00:00Z   0      8.0   1991-09-15 01:00:00\n\n  // input 1991-04-14 02:30:00, found row 4,\n  //  4  1991-04-13 18:00:00Z   1      9.0   1991-04-14 03:00:00  1991-04-14 01:59:59\n  if (prior_second < localtime)\n  {\n    // it's a skip\n    // printf(\"SKIP: prev %ld local %ld start %ld\\n\", prior_second, localtime, transI->localtime);\n    if (postTransition)\n    {\n      return &localtimes[transI->localtimeIdx];\n    }\n    else\n    {\n      return &localtimes[prior_trans.localtimeIdx];\n    }\n  }\n\n  // input 1990-09-16 01:30:00, found row 4, looking at row 3\n  //  3  1990-09-15 17:00:00Z   0      8.0   1990-09-16 01:00:00  1990-09-16 01:59:59\n  --transI;\n  if (transI != transitions.begin())\n  {\n    prior_trans = *(transI - 1);\n    prior_second = transI->utctime - 1 + localtimes[prior_trans.localtimeIdx].utcOffset;\n  }\n  if (localtime <= prior_second)\n  {\n    // it's repeat\n    // printf(\"REPEAT: prev %ld local %ld start %ld\\n\", prior_second, localtime, transI->localtime);\n    if (postTransition)\n    {\n      return &localtimes[transI->localtimeIdx];\n    }\n    else\n    {\n      return &localtimes[prior_trans.localtimeIdx];\n    }\n  }\n\n  // otherwise, it's unique\n  return &localtimes[transI->localtimeIdx];\n}\n\n// static\nTimeZone TimeZone::UTC()\n{\n  return TimeZone(0, \"UTC\");\n}\n\n// static\nTimeZone TimeZone::loadZoneFile(const char* zonefile)\n{\n  std::unique_ptr<Data> data(new Data);\n  if (!detail::readTimeZoneFile(zonefile, data.get()))\n  {\n    data.reset();\n  }\n  return TimeZone(std::move(data));\n}\n\nTimeZone::TimeZone(std::unique_ptr<Data> data)\n  : data_(std::move(data))\n{\n}\n\nTimeZone::TimeZone(int eastOfUtc, const char* name)\n  : data_(new TimeZone::Data)\n{\n  data_->addLocalTime(eastOfUtc, false, 0);\n  data_->abbreviation = name;\n}\n\nstruct DateTime TimeZone::toLocalTime(int64_t seconds, int* utcOffset) const\n{\n  struct DateTime localTime;\n  assert(data_ != NULL);\n\n  const Data::LocalTime* local = data_->findLocalTime(seconds);\n\n  if (local)\n  {\n    localTime = detail::BreakTime(seconds + local->utcOffset);\n    if (utcOffset)\n    {\n      *utcOffset = local->utcOffset;\n    }\n  }\n\n  return localTime;\n}\n\nint64_t TimeZone::fromLocalTime(const struct DateTime& localtime, bool postTransition) const\n{\n  assert(data_ != NULL);\n  const Data::LocalTime* local = data_->findLocalTime(localtime, postTransition);\n  const int64_t localSeconds = fromUtcTime(localtime);\n  if (local)\n  {\n    return localSeconds - local->utcOffset;\n  }\n  // fallback as if it's UTC time.\n  return localSeconds;\n}\n\nDateTime TimeZone::toUtcTime(int64_t secondsSinceEpoch)\n{\n  return detail::BreakTime(secondsSinceEpoch);\n}\n\nint64_t TimeZone::fromUtcTime(const DateTime& dt)\n{\n  Date date(dt.year, dt.month, dt.day);\n  int secondsInDay = dt.hour * 3600 + dt.minute * 60 + dt.second;\n  int64_t days = date.julianDayNumber() - Date::kJulianDayOf1970_01_01;\n  return days * kSecondsPerDay + secondsInDay;\n}\n\n\nDateTime::DateTime(const struct tm& t)\n  : year(t.tm_year + 1900), month(t.tm_mon + 1), day(t.tm_mday),\n    hour(t.tm_hour), minute(t.tm_min), second(t.tm_sec)\n{\n}\n\nstring DateTime::toIsoString() const\n{\n  char buf[64];\n  snprintf(buf, sizeof buf, \"%04d-%02d-%02d %02d:%02d:%02d\",\n           year, month, day, hour, minute, second);\n  return buf;\n}\n"
  },
  {
    "path": "muduo/base/TimeZone.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_TIMEZONE_H\n#define MUDUO_BASE_TIMEZONE_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/Types.h\"\n#include <memory>\n#include <time.h>\n\nnamespace muduo\n{\n\n// Local time in unspecified timezone.\n// A minute is always 60 seconds, no leap seconds.\nstruct DateTime\n{\n  DateTime() {}\n  explicit DateTime(const struct tm&);\n  DateTime(int _year, int _month, int _day, int _hour, int _minute, int _second)\n      : year(_year), month(_month), day(_day), hour(_hour), minute(_minute), second(_second)\n  {\n  }\n\n  // \"2011-12-31 12:34:56\"\n  string toIsoString() const;\n\n  int year = 0;     // [1900, 2500]\n  int month = 0;    // [1, 12]\n  int day = 0;      // [1, 31]\n  int hour = 0;     // [0, 23]\n  int minute = 0;   // [0, 59]\n  int second = 0;   // [0, 59]\n};\n\n// TimeZone for 1970~2100\nclass TimeZone : public muduo::copyable\n{\n public:\n  TimeZone() = default;  // an invalid timezone\n  TimeZone(int eastOfUtc, const char* tzname);  // a fixed timezone\n\n  static TimeZone UTC();\n  static TimeZone China();  // Fixed at GMT+8, no DST\n  static TimeZone loadZoneFile(const char* zonefile);\n\n  // default copy ctor/assignment/dtor are Okay.\n\n  bool valid() const\n  {\n    // 'explicit operator bool() const' in C++11\n    return static_cast<bool>(data_);\n  }\n\n  struct DateTime toLocalTime(int64_t secondsSinceEpoch, int* utcOffset = nullptr) const;\n  int64_t fromLocalTime(const struct DateTime&, bool postTransition = false) const;\n\n  // gmtime(3)\n  static struct DateTime toUtcTime(int64_t secondsSinceEpoch);\n  // timegm(3)\n  static int64_t fromUtcTime(const struct DateTime&);\n\n  struct Data;\n\n private:\n  explicit TimeZone(std::unique_ptr<Data> data);\n\n  std::shared_ptr<Data> data_;\n\n  friend class TimeZoneTestPeer;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_TIMEZONE_H\n"
  },
  {
    "path": "muduo/base/Timestamp.cc",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Timestamp.h\"\n\n#include <sys/time.h>\n#include <stdio.h>\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <inttypes.h>\n\nusing namespace muduo;\n\nstatic_assert(sizeof(Timestamp) == sizeof(int64_t),\n              \"Timestamp should be same size as int64_t\");\n\nstring Timestamp::toString() const\n{\n  char buf[32] = {0};\n  int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond;\n  int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond;\n  snprintf(buf, sizeof(buf), \"%\" PRId64 \".%06\" PRId64 \"\", seconds, microseconds);\n  return buf;\n}\n\nstring Timestamp::toFormattedString(bool showMicroseconds) const\n{\n  char buf[64] = {0};\n  time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);\n  struct tm tm_time;\n  gmtime_r(&seconds, &tm_time);\n\n  if (showMicroseconds)\n  {\n    int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);\n    snprintf(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d.%06d\",\n             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n             tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,\n             microseconds);\n  }\n  else\n  {\n    snprintf(buf, sizeof(buf), \"%4d%02d%02d %02d:%02d:%02d\",\n             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\n             tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);\n  }\n  return buf;\n}\n\nTimestamp Timestamp::now()\n{\n  struct timeval tv;\n  gettimeofday(&tv, NULL);\n  int64_t seconds = tv.tv_sec;\n  return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);\n}\n\n"
  },
  {
    "path": "muduo/base/Timestamp.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_TIMESTAMP_H\n#define MUDUO_BASE_TIMESTAMP_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/Types.h\"\n\n#include <boost/operators.hpp>\n\nnamespace muduo\n{\n\n///\n/// Time stamp in UTC, in microseconds resolution.\n///\n/// This class is immutable.\n/// It's recommended to pass it by value, since it's passed in register on x64.\n///\nclass Timestamp : public muduo::copyable,\n                  public boost::equality_comparable<Timestamp>,\n                  public boost::less_than_comparable<Timestamp>\n{\n public:\n  ///\n  /// Constucts an invalid Timestamp.\n  ///\n  Timestamp()\n    : microSecondsSinceEpoch_(0)\n  {\n  }\n\n  ///\n  /// Constucts a Timestamp at specific time\n  ///\n  /// @param microSecondsSinceEpoch\n  explicit Timestamp(int64_t microSecondsSinceEpochArg)\n    : microSecondsSinceEpoch_(microSecondsSinceEpochArg)\n  {\n  }\n\n  void swap(Timestamp& that)\n  {\n    std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_);\n  }\n\n  // default copy/assignment/dtor are Okay\n\n  string toString() const;\n  string toFormattedString(bool showMicroseconds = true) const;\n\n  bool valid() const { return microSecondsSinceEpoch_ > 0; }\n\n  // for internal usage.\n  int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }\n  time_t secondsSinceEpoch() const\n  { return static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); }\n\n  ///\n  /// Get time of now.\n  ///\n  static Timestamp now();\n  static Timestamp invalid()\n  {\n    return Timestamp();\n  }\n\n  static Timestamp fromUnixTime(time_t t)\n  {\n    return fromUnixTime(t, 0);\n  }\n\n  static Timestamp fromUnixTime(time_t t, int microseconds)\n  {\n    return Timestamp(static_cast<int64_t>(t) * kMicroSecondsPerSecond + microseconds);\n  }\n\n  static const int kMicroSecondsPerSecond = 1000 * 1000;\n\n private:\n  int64_t microSecondsSinceEpoch_;\n};\n\ninline bool operator<(Timestamp lhs, Timestamp rhs)\n{\n  return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();\n}\n\ninline bool operator==(Timestamp lhs, Timestamp rhs)\n{\n  return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();\n}\n\n///\n/// Gets time difference of two timestamps, result in seconds.\n///\n/// @param high, low\n/// @return (high-low) in seconds\n/// @c double has 52-bit precision, enough for one-microsecond\n/// resolution for next 100 years.\ninline double timeDifference(Timestamp high, Timestamp low)\n{\n  int64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch();\n  return static_cast<double>(diff) / Timestamp::kMicroSecondsPerSecond;\n}\n\n///\n/// Add @c seconds to given timestamp.\n///\n/// @return timestamp+seconds as Timestamp\n///\ninline Timestamp addTime(Timestamp timestamp, double seconds)\n{\n  int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);\n  return Timestamp(timestamp.microSecondsSinceEpoch() + delta);\n}\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_TIMESTAMP_H\n"
  },
  {
    "path": "muduo/base/Types.h",
    "content": "#ifndef MUDUO_BASE_TYPES_H\n#define MUDUO_BASE_TYPES_H\n\n#include <stdint.h>\n#include <string.h>  // memset\n#include <string>\n\n#ifndef NDEBUG\n#include <assert.h>\n#endif\n\n///\n/// The most common stuffs.\n///\nnamespace muduo\n{\n\nusing std::string;\n\ninline void memZero(void* p, size_t n)\n{\n  memset(p, 0, n);\n}\n\n// Taken from google-protobuf stubs/common.h\n//\n// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc.  All rights reserved.\n// http://code.google.com/p/protobuf/\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//     * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Author: kenton@google.com (Kenton Varda) and others\n//\n// Contains basic types and utilities used by the rest of the library.\n\n//\n// Use implicit_cast as a safe version of static_cast or const_cast\n// for upcasting in the type hierarchy (i.e. casting a pointer to Foo\n// to a pointer to SuperclassOfFoo or casting a pointer to Foo to\n// a const pointer to Foo).\n// When you use implicit_cast, the compiler checks that the cast is safe.\n// Such explicit implicit_casts are necessary in surprisingly many\n// situations where C++ demands an exact type match instead of an\n// argument type convertable to a target type.\n//\n// The From type can be inferred, so the preferred syntax for using\n// implicit_cast is the same as for static_cast etc.:\n//\n//   implicit_cast<ToType>(expr)\n//\n// implicit_cast would have been part of the C++ standard library,\n// but the proposal was submitted too late.  It will probably make\n// its way into the language in the future.\ntemplate<typename To, typename From>\ninline To implicit_cast(From const &f)\n{\n  return f;\n}\n\n// When you upcast (that is, cast a pointer from type Foo to type\n// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts\n// always succeed.  When you downcast (that is, cast a pointer from\n// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because\n// how do you know the pointer is really of type SubclassOfFoo?  It\n// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,\n// when you downcast, you should use this macro.  In debug mode, we\n// use dynamic_cast<> to double-check the downcast is legal (we die\n// if it's not).  In normal mode, we do the efficient static_cast<>\n// instead.  Thus, it's important to test in debug mode to make sure\n// the cast is legal!\n//    This is the only place in the code we should use dynamic_cast<>.\n// In particular, you SHOULDN'T be using dynamic_cast<> in order to\n// do RTTI (eg code like this:\n//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);\n//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);\n// You should design the code some other way not to need this.\n\ntemplate<typename To, typename From>     // use like this: down_cast<T*>(foo);\ninline To down_cast(From* f)                     // so we only accept pointers\n{\n  // Ensures that To is a sub-type of From *.  This test is here only\n  // for compile-time type checking, and has no overhead in an\n  // optimized build at run-time, as it will be optimized away\n  // completely.\n  if (false)\n  {\n    implicit_cast<From*, To>(0);\n  }\n\n#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)\n  assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!\n#endif\n  return static_cast<To>(f);\n}\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_TYPES_H\n"
  },
  {
    "path": "muduo/base/WeakCallback.h",
    "content": "// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n//\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef MUDUO_BASE_WEAKCALLBACK_H\n#define MUDUO_BASE_WEAKCALLBACK_H\n\n#include <functional>\n#include <memory>\n\nnamespace muduo\n{\n\n// A barely usable WeakCallback\n\ntemplate<typename CLASS, typename... ARGS>\nclass WeakCallback\n{\n public:\n\n  WeakCallback(const std::weak_ptr<CLASS>& object,\n               const std::function<void (CLASS*, ARGS...)>& function)\n    : object_(object), function_(function)\n  {\n  }\n\n  // Default dtor, copy ctor and assignment are okay\n\n  void operator()(ARGS&&... args) const\n  {\n    std::shared_ptr<CLASS> ptr(object_.lock());\n    if (ptr)\n    {\n      function_(ptr.get(), std::forward<ARGS>(args)...);\n    }\n    // else\n    // {\n    //   LOG_TRACE << \"expired\";\n    // }\n  }\n\n private:\n\n  std::weak_ptr<CLASS> object_;\n  std::function<void (CLASS*, ARGS...)> function_;\n};\n\ntemplate<typename CLASS, typename... ARGS>\nWeakCallback<CLASS, ARGS...> makeWeakCallback(const std::shared_ptr<CLASS>& object,\n                                              void (CLASS::*function)(ARGS...))\n{\n  return WeakCallback<CLASS, ARGS...>(object, function);\n}\n\ntemplate<typename CLASS, typename... ARGS>\nWeakCallback<CLASS, ARGS...> makeWeakCallback(const std::shared_ptr<CLASS>& object,\n                                              void (CLASS::*function)(ARGS...) const)\n{\n  return WeakCallback<CLASS, ARGS...>(object, function);\n}\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_WEAKCALLBACK_H\n"
  },
  {
    "path": "muduo/base/copyable.h",
    "content": "#ifndef MUDUO_BASE_COPYABLE_H\n#define MUDUO_BASE_COPYABLE_H\n\nnamespace muduo\n{\n\n/// A tag class emphasises the objects are copyable.\n/// The empty base class optimization applies.\n/// Any derived class of copyable should be a value type.\nclass copyable\n{\n protected:\n  copyable() = default;\n  ~copyable() = default;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_COPYABLE_H\n"
  },
  {
    "path": "muduo/base/noncopyable.h",
    "content": "#ifndef MUDUO_BASE_NONCOPYABLE_H\n#define MUDUO_BASE_NONCOPYABLE_H\n\nnamespace muduo\n{\n\nclass noncopyable\n{\n public:\n  noncopyable(const noncopyable&) = delete;\n  void operator=(const noncopyable&) = delete;\n\n protected:\n  noncopyable() = default;\n  ~noncopyable() = default;\n};\n\n}  // namespace muduo\n\n#endif  // MUDUO_BASE_NONCOPYABLE_H\n"
  },
  {
    "path": "muduo/base/tests/AsyncLogging_test.cc",
    "content": "#include \"muduo/base/AsyncLogging.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <stdio.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\noff_t kRollSize = 500*1000*1000;\n\nmuduo::AsyncLogging* g_asyncLog = NULL;\n\nvoid asyncOutput(const char* msg, int len)\n{\n  g_asyncLog->append(msg, len);\n}\n\nvoid bench(bool longLog)\n{\n  muduo::Logger::setOutput(asyncOutput);\n\n  int cnt = 0;\n  const int kBatch = 1000;\n  muduo::string empty = \" \";\n  muduo::string longStr(3000, 'X');\n  longStr += \" \";\n\n  for (int t = 0; t < 30; ++t)\n  {\n    muduo::Timestamp start = muduo::Timestamp::now();\n    for (int i = 0; i < kBatch; ++i)\n    {\n      LOG_INFO << \"Hello 0123456789\" << \" abcdefghijklmnopqrstuvwxyz \"\n               << (longLog ? longStr : empty)\n               << cnt;\n      ++cnt;\n    }\n    muduo::Timestamp end = muduo::Timestamp::now();\n    printf(\"%f\\n\", timeDifference(end, start)*1000000/kBatch);\n    struct timespec ts = { 0, 500*1000*1000 };\n    nanosleep(&ts, NULL);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  {\n    // set max virtual memory to 2GB.\n    size_t kOneGB = 1000*1024*1024;\n    rlimit rl = { 2*kOneGB, 2*kOneGB };\n    setrlimit(RLIMIT_AS, &rl);\n  }\n\n  printf(\"pid = %d\\n\", getpid());\n\n  char name[256] = { '\\0' };\n  strncpy(name, argv[0], sizeof name - 1);\n  muduo::AsyncLogging log(::basename(name), kRollSize);\n  log.start();\n  g_asyncLog = &log;\n\n  bool longLog = argc > 1;\n  bench(longLog);\n}\n"
  },
  {
    "path": "muduo/base/tests/Atomic_unittest.cc",
    "content": "#include \"muduo/base/Atomic.h\"\n#include <assert.h>\n\nint main()\n{\n  {\n  muduo::AtomicInt64 a0;\n  assert(a0.get() == 0);\n  assert(a0.getAndAdd(1) == 0);\n  assert(a0.get() == 1);\n  assert(a0.addAndGet(2) == 3);\n  assert(a0.get() == 3);\n  assert(a0.incrementAndGet() == 4);\n  assert(a0.get() == 4);\n  a0.increment();\n  assert(a0.get() == 5);\n  assert(a0.addAndGet(-3) == 2);\n  assert(a0.getAndSet(100) == 2);\n  assert(a0.get() == 100);\n  }\n\n  {\n  muduo::AtomicInt32 a1;\n  assert(a1.get() == 0);\n  assert(a1.getAndAdd(1) == 0);\n  assert(a1.get() == 1);\n  assert(a1.addAndGet(2) == 3);\n  assert(a1.get() == 3);\n  assert(a1.incrementAndGet() == 4);\n  assert(a1.get() == 4);\n  a1.increment();\n  assert(a1.get() == 5);\n  assert(a1.addAndGet(-3) == 2);\n  assert(a1.getAndSet(100) == 2);\n  assert(a1.get() == 100);\n  }\n}\n\n"
  },
  {
    "path": "muduo/base/tests/BlockingQueue_bench.cc",
    "content": "#include \"muduo/base/BlockingQueue.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <map>\n#include <string>\n#include <vector>\n#include <stdio.h>\n#include <unistd.h>\n\nbool g_verbose = false;\n\n// Many threads, one queue.\nclass Bench\n{\n public:\n  Bench(int numThreads)\n    : latch_(numThreads)\n  {\n    threads_.reserve(numThreads);\n    for (int i = 0; i < numThreads; ++i)\n    {\n      char name[32];\n      snprintf(name, sizeof name, \"work thread %d\", i);\n      threads_.emplace_back(new muduo::Thread(\n            std::bind(&Bench::threadFunc, this), muduo::string(name)));\n    }\n    for (auto& thr : threads_)\n    {\n      thr->start();\n    }\n  }\n\n  void run(int times)\n  {\n    printf(\"waiting for count down latch\\n\");\n    latch_.wait();\n    LOG_INFO << threads_.size() << \" threads started\";\n    int64_t total_delay = 0;\n    for (int i = 0; i < times; ++i)\n    {\n      muduo::Timestamp now(muduo::Timestamp::now());\n      queue_.put(now);\n      total_delay += delay_queue_.take();\n    }\n    printf(\"Average delay: %.3fus\\n\", static_cast<double>(total_delay) / times);\n  }\n\n  void joinAll()\n  {\n    for (size_t i = 0; i < threads_.size(); ++i)\n    {\n      queue_.put(muduo::Timestamp::invalid());\n    }\n\n    for (auto& thr : threads_)\n    {\n      thr->join();\n    }\n    LOG_INFO << threads_.size() << \" threads stopped\";\n  }\n\n private:\n\n  void threadFunc()\n  {\n    if (g_verbose) {\n    printf(\"tid=%d, %s started\\n\",\n           muduo::CurrentThread::tid(),\n           muduo::CurrentThread::name());\n    }\n\n    std::map<int, int> delays;\n    latch_.countDown();\n    bool running = true;\n    while (running)\n    {\n      muduo::Timestamp t(queue_.take());\n      muduo::Timestamp now(muduo::Timestamp::now());\n      if (t.valid())\n      {\n        int delay = static_cast<int>(timeDifference(now, t) * 1000000);\n        // printf(\"tid=%d, latency = %d us\\n\",\n        //        muduo::CurrentThread::tid(), delay);\n        ++delays[delay];\n        delay_queue_.put(delay);\n      }\n      running = t.valid();\n    }\n\n    if (g_verbose)\n    {\n      printf(\"tid=%d, %s stopped\\n\",\n             muduo::CurrentThread::tid(),\n             muduo::CurrentThread::name());\n      for (const auto& delay : delays)\n      {\n        printf(\"tid = %d, delay = %d, count = %d\\n\",\n               muduo::CurrentThread::tid(),\n               delay.first, delay.second);\n      }\n    }\n  }\n\n  muduo::BlockingQueue<muduo::Timestamp> queue_;\n  muduo::BlockingQueue<int> delay_queue_;\n  muduo::CountDownLatch latch_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n};\n\nint main(int argc, char* argv[])\n{\n  int threads = argc > 1 ? atoi(argv[1]) : 1;\n\n  Bench t(threads);\n  t.run(100000);\n  t.joinAll();\n}\n"
  },
  {
    "path": "muduo/base/tests/BlockingQueue_bench2.cc",
    "content": "#include \"muduo/base/BlockingQueue.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <map>\n#include <string>\n#include <vector>\n#include <stdio.h>\n#include <unistd.h>\n\n// hot potato benchmarking https://en.wikipedia.org/wiki/Hot_potato\n// N threads, one hot potato.\nclass Bench\n{\n public:\n  Bench(int numThreads)\n    : startLatch_(numThreads),\n      stopLatch_(1)\n  {\n    queues_.reserve(numThreads);\n    threads_.reserve(numThreads);\n    for (int i = 0; i < numThreads; ++i)\n    {\n      queues_.emplace_back(new muduo::BlockingQueue<int>());\n      char name[32];\n      snprintf(name, sizeof name, \"work thread %d\", i);\n      threads_.emplace_back(new muduo::Thread(\n            [this, i] { threadFunc(i); },\n            muduo::string(name)));\n    }\n  }\n\n  void Start()\n  {\n    muduo::Timestamp start = muduo::Timestamp::now();\n    for (auto& thr : threads_)\n    {\n      thr->start();\n    }\n    startLatch_.wait();\n    muduo::Timestamp started = muduo::Timestamp::now();\n    printf(\"all %zd threads started, %.3fms\\n\",\n           threads_.size(), 1e3 * timeDifference(started, start));\n  }\n\n  void Run()\n  {\n    muduo::Timestamp start = muduo::Timestamp::now();\n    const int rounds = 100003;\n    queues_[0]->put(rounds);\n\n    auto done = done_.take();\n    double elapsed = timeDifference(done.second, start);\n    printf(\"thread id=%d done, total %.3fms, %.3fus / round\\n\",\n           done.first, 1e3 * elapsed, 1e6 * elapsed / rounds);\n  }\n\n  void Stop()\n  {\n    muduo::Timestamp stop = muduo::Timestamp::now();\n    for (const auto& queue : queues_)\n    {\n      queue->put(-1);\n    }\n    for (auto& thr : threads_)\n    {\n      thr->join();\n    }\n\n    muduo::Timestamp t2 = muduo::Timestamp::now();\n    printf(\"all %zd threads joined, %.3fms\\n\",\n           threads_.size(), 1e3 * timeDifference(t2, stop));\n  }\n\n private:\n  void threadFunc(int id)\n  {\n    startLatch_.countDown();\n\n    muduo::BlockingQueue<int>* input = queues_[id].get();\n    muduo::BlockingQueue<int>* output = queues_[(id+1) % queues_.size()].get();\n    while (true)\n    {\n      int value = input->take();\n      if (value > 0)\n      {\n        output->put(value - 1);\n        if (verbose_)\n        {\n          // printf(\"thread %d, got %d\\n\", id, value);\n        }\n        continue;\n      }\n\n      if (value == 0)\n      {\n        done_.put(std::make_pair(id, muduo::Timestamp::now()));\n      }\n      break;\n    }\n  }\n\n  using TimestampQueue = muduo::BlockingQueue<std::pair<int, muduo::Timestamp>>;\n  TimestampQueue done_;\n  muduo::CountDownLatch startLatch_, stopLatch_;\n  std::vector<std::unique_ptr<muduo::BlockingQueue<int>>> queues_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n  const bool verbose_ = true;\n};\n\nint main(int argc, char* argv[])\n{\n  int threads = argc > 1 ? atoi(argv[1]) : 1;\n\n  printf(\"sizeof BlockingQueue = %zd\\n\", sizeof(muduo::BlockingQueue<int>));\n  printf(\"sizeof deque<int> = %zd\\n\", sizeof(std::deque<int>));\n  Bench t(threads);\n  t.Start();\n  t.Run();\n  t.Stop();\n  // exit(0);\n}\n"
  },
  {
    "path": "muduo/base/tests/BlockingQueue_test.cc",
    "content": "#include \"muduo/base/BlockingQueue.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <memory>\n#include <string>\n#include <vector>\n#include <stdio.h>\n#include <unistd.h>\n\nclass Test\n{\n public:\n  Test(int numThreads)\n    : latch_(numThreads)\n  {\n    for (int i = 0; i < numThreads; ++i)\n    {\n      char name[32];\n      snprintf(name, sizeof name, \"work thread %d\", i);\n      threads_.emplace_back(new muduo::Thread(\n            std::bind(&Test::threadFunc, this), muduo::string(name)));\n    }\n    for (auto& thr : threads_)\n    {\n      thr->start();\n    }\n  }\n\n  void run(int times)\n  {\n    printf(\"waiting for count down latch\\n\");\n    latch_.wait();\n    printf(\"all threads started\\n\");\n    for (int i = 0; i < times; ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"hello %d\", i);\n      queue_.put(buf);\n      printf(\"tid=%d, put data = %s, size = %zd\\n\", muduo::CurrentThread::tid(), buf, queue_.size());\n    }\n  }\n\n  void joinAll()\n  {\n    for (size_t i = 0; i < threads_.size(); ++i)\n    {\n      queue_.put(\"stop\");\n    }\n\n    for (auto& thr : threads_)\n    {\n      thr->join();\n    }\n  }\n\n private:\n\n  void threadFunc()\n  {\n    printf(\"tid=%d, %s started\\n\",\n           muduo::CurrentThread::tid(),\n           muduo::CurrentThread::name());\n\n    latch_.countDown();\n    bool running = true;\n    while (running)\n    {\n      std::string d(queue_.take());\n      printf(\"tid=%d, get data = %s, size = %zd\\n\", muduo::CurrentThread::tid(), d.c_str(), queue_.size());\n      running = (d != \"stop\");\n    }\n\n    printf(\"tid=%d, %s stopped\\n\",\n           muduo::CurrentThread::tid(),\n           muduo::CurrentThread::name());\n  }\n\n  muduo::BlockingQueue<std::string> queue_;\n  muduo::CountDownLatch latch_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n};\n\nvoid testMove()\n{\n  muduo::BlockingQueue<std::unique_ptr<int>> queue;\n  queue.put(std::unique_ptr<int>(new int(42)));\n  std::unique_ptr<int> x = queue.take();\n  printf(\"took %d\\n\", *x);\n  *x = 123;\n  queue.put(std::move(x));\n  std::unique_ptr<int> y = queue.take();\n  printf(\"took %d\\n\", *y);\n}\n\nint main()\n{\n  printf(\"pid=%d, tid=%d\\n\", ::getpid(), muduo::CurrentThread::tid());\n  Test t(5);\n  t.run(100);\n  t.joinAll();\n\n  testMove();\n\n  printf(\"number of created threads %d\\n\", muduo::Thread::numCreated());\n}\n"
  },
  {
    "path": "muduo/base/tests/BoundedBlockingQueue_test.cc",
    "content": "#include \"muduo/base/BoundedBlockingQueue.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <string>\n#include <vector>\n\n#include <stdio.h>\n#include <unistd.h>\n\nclass Test\n{\n public:\n  Test(int numThreads)\n    : queue_(20),\n      latch_(numThreads)\n  {\n    threads_.reserve(numThreads);\n    for (int i = 0; i < numThreads; ++i)\n    {\n      char name[32];\n      snprintf(name, sizeof name, \"work thread %d\", i);\n      threads_.emplace_back(new muduo::Thread(\n            std::bind(&Test::threadFunc, this), muduo::string(name)));\n    }\n    for (auto& thr : threads_)\n    {\n      thr->start();\n    }\n  }\n\n  void run(int times)\n  {\n    printf(\"waiting for count down latch\\n\");\n    latch_.wait();\n    printf(\"all threads started\\n\");\n    for (int i = 0; i < times; ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"hello %d\", i);\n      queue_.put(buf);\n      printf(\"tid=%d, put data = %s, size = %zd\\n\", muduo::CurrentThread::tid(), buf, queue_.size());\n    }\n  }\n\n  void joinAll()\n  {\n    for (size_t i = 0; i < threads_.size(); ++i)\n    {\n      queue_.put(\"stop\");\n    }\n\n    for (auto& thr : threads_)\n    {\n      thr->join();\n    }\n  }\n\n private:\n\n  void threadFunc()\n  {\n    printf(\"tid=%d, %s started\\n\",\n           muduo::CurrentThread::tid(),\n           muduo::CurrentThread::name());\n\n    latch_.countDown();\n    bool running = true;\n    while (running)\n    {\n      std::string d(queue_.take());\n      printf(\"tid=%d, get data = %s, size = %zd\\n\", muduo::CurrentThread::tid(), d.c_str(), queue_.size());\n      running = (d != \"stop\");\n    }\n\n    printf(\"tid=%d, %s stopped\\n\",\n           muduo::CurrentThread::tid(),\n           muduo::CurrentThread::name());\n  }\n\n  muduo::BoundedBlockingQueue<std::string> queue_;\n  muduo::CountDownLatch latch_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n};\n\nvoid testMove()\n{\n#if BOOST_VERSION >= 105500L\n  muduo::BoundedBlockingQueue<std::unique_ptr<int>> queue(10);\n  queue.put(std::unique_ptr<int>(new int(42)));\n  std::unique_ptr<int> x = queue.take();\n  printf(\"took %d\\n\", *x);\n  *x = 123;\n  queue.put(std::move(x));\n  std::unique_ptr<int> y;\n  y = queue.take();\n  printf(\"took %d\\n\", *y);\n#endif\n}\n\nint main()\n{\n  printf(\"pid=%d, tid=%d\\n\", ::getpid(), muduo::CurrentThread::tid());\n  testMove();\n  Test t(5);\n  t.run(100);\n  t.joinAll();\n\n  printf(\"number of created threads %d\\n\", muduo::Thread::numCreated());\n}\n"
  },
  {
    "path": "muduo/base/tests/CMakeLists.txt",
    "content": "add_executable(asynclogging_test AsyncLogging_test.cc)\ntarget_link_libraries(asynclogging_test muduo_base)\n\nadd_executable(atomic_unittest Atomic_unittest.cc)\nadd_test(NAME atomic_unittest COMMAND atomic_unittest)\n\nadd_executable(blockingqueue_test BlockingQueue_test.cc)\ntarget_link_libraries(blockingqueue_test muduo_base)\n\nadd_executable(blockingqueue_bench BlockingQueue_bench.cc)\ntarget_link_libraries(blockingqueue_bench muduo_base)\n\nadd_executable(blockingqueue_bench2 BlockingQueue_bench2.cc)\ntarget_link_libraries(blockingqueue_bench2 muduo_base)\n# set_target_properties(blockingqueue_bench2 PROPERTIES COMPILE_FLAGS \"-std=c++17\")\n\nadd_executable(boundedblockingqueue_test BoundedBlockingQueue_test.cc)\ntarget_link_libraries(boundedblockingqueue_test muduo_base)\n\nadd_executable(date_unittest Date_unittest.cc)\ntarget_link_libraries(date_unittest muduo_base)\nadd_test(NAME date_unittest COMMAND date_unittest)\n\nadd_executable(exception_test Exception_test.cc)\ntarget_link_libraries(exception_test muduo_base)\nadd_test(NAME exception_test COMMAND exception_test)\n\nadd_executable(fileutil_test FileUtil_test.cc)\ntarget_link_libraries(fileutil_test muduo_base)\nadd_test(NAME fileutil_test COMMAND fileutil_test)\n\nadd_executable(fork_test Fork_test.cc)\ntarget_link_libraries(fork_test muduo_base)\n\nif(ZLIB_FOUND)\n  add_executable(gzipfile_test GzipFile_test.cc)\n  target_link_libraries(gzipfile_test muduo_base z)\n  add_test(NAME gzipfile_test COMMAND gzipfile_test)\nendif()\n\nadd_executable(logfile_test LogFile_test.cc)\ntarget_link_libraries(logfile_test muduo_base)\n\nadd_executable(logging_test Logging_test.cc)\ntarget_link_libraries(logging_test muduo_base)\n\nadd_executable(logstream_bench LogStream_bench.cc)\ntarget_link_libraries(logstream_bench muduo_base)\n\nif(BOOSTTEST_LIBRARY)\nadd_executable(logstream_test LogStream_test.cc)\ntarget_link_libraries(logstream_test muduo_base boost_unit_test_framework)\nadd_test(NAME logstream_test COMMAND logstream_test)\nendif()\n\nadd_executable(mutex_test Mutex_test.cc)\ntarget_link_libraries(mutex_test muduo_base)\n\nadd_executable(processinfo_test ProcessInfo_test.cc)\ntarget_link_libraries(processinfo_test muduo_base)\n\nadd_executable(singleton_test Singleton_test.cc)\ntarget_link_libraries(singleton_test muduo_base)\n\nadd_executable(singleton_threadlocal_test SingletonThreadLocal_test.cc)\ntarget_link_libraries(singleton_threadlocal_test muduo_base)\n\nadd_executable(thread_bench Thread_bench.cc)\ntarget_link_libraries(thread_bench muduo_base)\n\nadd_executable(thread_test Thread_test.cc)\ntarget_link_libraries(thread_test muduo_base)\n\nadd_executable(threadlocal_test ThreadLocal_test.cc)\ntarget_link_libraries(threadlocal_test muduo_base)\n\nadd_executable(threadlocalsingleton_test ThreadLocalSingleton_test.cc)\ntarget_link_libraries(threadlocalsingleton_test muduo_base)\n\nadd_executable(threadpool_test ThreadPool_test.cc)\ntarget_link_libraries(threadpool_test muduo_base)\n\nadd_executable(timestamp_unittest Timestamp_unittest.cc)\ntarget_link_libraries(timestamp_unittest muduo_base)\nadd_test(NAME timestamp_unittest COMMAND timestamp_unittest)\n\nadd_executable(timezone_unittest TimeZone_unittest.cc)\ntarget_link_libraries(timezone_unittest muduo_base)\nadd_test(NAME timezone_unittest COMMAND timezone_unittest)\n\nadd_executable(timezone_util TimeZone_util.cc)\ntarget_link_libraries(timezone_util muduo_base)\n\n"
  },
  {
    "path": "muduo/base/tests/Date_unittest.cc",
    "content": "#include \"muduo/base/Date.h\"\n#include <assert.h>\n#include <stdio.h>\n#include <time.h>\n\nusing muduo::Date;\n\nconst int kMonthsOfYear = 12;\n\nint isLeapYear(int year)\n{\n  if (year % 400 == 0)\n    return 1;\n  else if (year % 100 == 0)\n    return 0;\n  else if (year % 4 == 0)\n    return 1;\n  else\n    return 0;\n}\n\nint daysOfMonth(int year, int month)\n{\n  static int days[2][kMonthsOfYear+1] =\n  {\n    { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },\n    { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },\n  };\n  return days[isLeapYear(year)][month];\n}\n\nvoid passByConstReference(const Date& x)\n{\n  printf(\"%s\\n\", x.toIsoString().c_str());\n}\n\nvoid passByValue(Date x)\n{\n  printf(\"%s\\n\", x.toIsoString().c_str());\n}\n\nint main()\n{\n  time_t now = time(NULL);\n  struct tm t1 = *gmtime(&now);\n  struct tm t2 = *localtime(&now);\n  Date someDay(2008, 9, 10);\n  printf(\"%s\\n\", someDay.toIsoString().c_str());\n  passByValue(someDay);\n  passByConstReference(someDay);\n  Date todayUtc(t1);\n  printf(\"%s\\n\", todayUtc.toIsoString().c_str());\n  Date todayLocal(t2);\n  printf(\"%s\\n\", todayLocal.toIsoString().c_str());\n\n  int julianDayNumber = 2415021;\n  int weekDay = 1; // Monday\n\n  for (int year = 1900; year < 2500; ++year)\n  {\n    assert(Date(year, 3, 1).julianDayNumber() - Date(year, 2, 29).julianDayNumber()\n           == isLeapYear(year));\n    for (int month = 1; month <= kMonthsOfYear; ++month)\n    {\n      for (int day = 1; day <= daysOfMonth(year, month); ++day)\n      {\n        Date d(year, month, day);\n        // printf(\"%s %d\\n\", d.toString().c_str(), d.weekDay());\n        assert(year == d.year());\n        assert(month == d.month());\n        assert(day == d.day());\n        assert(weekDay == d.weekDay());\n        assert(julianDayNumber == d.julianDayNumber());\n\n        Date d2(julianDayNumber);\n        assert(year == d2.year());\n        assert(month == d2.month());\n        assert(day == d2.day());\n        assert(weekDay == d2.weekDay());\n        assert(julianDayNumber == d2.julianDayNumber());\n\n        ++julianDayNumber;\n        weekDay = (weekDay+1) % 7;\n      }\n    }\n  }\n  printf(\"All passed.\\n\");\n}\n\n"
  },
  {
    "path": "muduo/base/tests/Exception_test.cc",
    "content": "#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Exception.h\"\n#include <functional>\n#include <vector>\n#include <stdio.h>\n\nclass Bar\n{\n public:\n  void test(std::vector<std::string> names = {})\n  {\n    printf(\"Stack:\\n%s\\n\", muduo::CurrentThread::stackTrace(true).c_str());\n    [] {\n      printf(\"Stack inside lambda:\\n%s\\n\", muduo::CurrentThread::stackTrace(true).c_str());\n    }();\n    std::function<void()> func([] {\n      printf(\"Stack inside std::function:\\n%s\\n\", muduo::CurrentThread::stackTrace(true).c_str());\n    });\n    func();\n\n    func = std::bind(&Bar::callback, this);\n    func();\n\n    throw muduo::Exception(\"oops\");\n  }\n\n private:\n   void callback()\n   {\n     printf(\"Stack inside std::bind:\\n%s\\n\", muduo::CurrentThread::stackTrace(true).c_str());\n   }\n};\n\nvoid foo()\n{\n  Bar b;\n  b.test();\n}\n\nint main()\n{\n  try\n  {\n    foo();\n  }\n  catch (const muduo::Exception& ex)\n  {\n    printf(\"reason: %s\\n\", ex.what());\n    printf(\"stack trace:\\n%s\\n\", ex.stackTrace());\n  }\n}\n"
  },
  {
    "path": "muduo/base/tests/FileUtil_test.cc",
    "content": "#include \"muduo/base/FileUtil.h\"\n\n#include <stdio.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n\nusing namespace muduo;\n\nint main()\n{\n  string result;\n  int64_t size = 0;\n  int err = FileUtil::readFile(\"/proc/self\", 1024, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/proc/self\", 1024, &result, NULL);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/proc/self/cmdline\", 1024, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/dev/null\", 1024, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/dev/zero\", 1024, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/notexist\", 1024, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/dev/zero\", 102400, &result, &size);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n  err = FileUtil::readFile(\"/dev/zero\", 102400, &result, NULL);\n  printf(\"%d %zd %\" PRIu64 \"\\n\", err, result.size(), size);\n}\n\n"
  },
  {
    "path": "muduo/base/tests/Fork_test.cc",
    "content": "#include \"muduo/base/CurrentThread.h\"\n\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nnamespace\n{\n__thread int x = 0;\n}\n\nvoid print()\n{\n  printf(\"pid=%d tid=%d x=%d\\n\", getpid(), muduo::CurrentThread::tid(), x);\n}\n\nint main()\n{\n  printf(\"parent %d\\n\", getpid());\n  print();\n  x = 1;\n  print();\n  pid_t p = fork();\n\n  if (p == 0)\n  {\n    printf(\"chlid %d\\n\", getpid());\n    // child\n    print();\n    x = 2;\n    print();\n\n    if (fork() == 0)\n    {\n      printf(\"grandchlid %d\\n\", getpid());\n      print();\n      x = 3;\n      print();\n    }\n  }\n  else\n  {\n    // parent\n    print();\n  }\n}\n"
  },
  {
    "path": "muduo/base/tests/GzipFile_test.cc",
    "content": "#include \"muduo/base/GzipFile.h\"\n\n#include \"muduo/base/Logging.h\"\n\nint main()\n{\n  const char* filename = \"/tmp/gzipfile_test.gz\";\n  ::unlink(filename);\n  const char data[] = \"123456789012345678901234567890123456789012345678901234567890\\n\";\n  {\n  muduo::GzipFile writer = muduo::GzipFile::openForAppend(filename);\n  if (writer.valid())\n  {\n    LOG_INFO << \"tell \" << writer.tell();\n    LOG_INFO << \"wrote \" << writer.write(data);\n    LOG_INFO << \"tell \" << writer.tell();\n  }\n  }\n\n  {\n  printf(\"testing reader\\n\");\n  muduo::GzipFile reader = muduo::GzipFile::openForRead(filename);\n  if (reader.valid())\n  {\n    char buf[256];\n    LOG_INFO << \"tell \" << reader.tell();\n    int nr = reader.read(buf, sizeof buf);\n    printf(\"read %d\\n\", nr);\n    if (nr >= 0)\n    {\n      buf[nr] = '\\0';\n      printf(\"data %s\", buf);\n    }\n    LOG_INFO << \"tell \" << reader.tell();\n    if (strncmp(buf, data, strlen(data)) != 0)\n    {\n      printf(\"failed!!!\\n\");\n      abort();\n    }\n    else\n    {\n      printf(\"PASSED\\n\");\n    }\n  }\n  }\n\n  {\n  muduo::GzipFile writer = muduo::GzipFile::openForWriteExclusive(filename);\n  if (writer.valid() || errno != EEXIST)\n  {\n    printf(\"FAILED\\n\");\n  }\n  }\n}\n"
  },
  {
    "path": "muduo/base/tests/LogFile_test.cc",
    "content": "#include \"muduo/base/LogFile.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <unistd.h>\n\nstd::unique_ptr<muduo::LogFile> g_logFile;\n\nvoid outputFunc(const char* msg, int len)\n{\n  g_logFile->append(msg, len);\n}\n\nvoid flushFunc()\n{\n  g_logFile->flush();\n}\n\nint main(int argc, char* argv[])\n{\n  char name[256] = { '\\0' };\n  strncpy(name, argv[0], sizeof name - 1);\n  g_logFile.reset(new muduo::LogFile(::basename(name), 200*1000));\n  muduo::Logger::setOutput(outputFunc);\n  muduo::Logger::setFlush(flushFunc);\n\n  muduo::string line = \"1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ \";\n\n  for (int i = 0; i < 10000; ++i)\n  {\n    LOG_INFO << line << i;\n\n    usleep(1000);\n  }\n}\n"
  },
  {
    "path": "muduo/base/tests/LogStream_bench.cc",
    "content": "#include \"muduo/base/LogStream.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <sstream>\n#include <stdio.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n\nusing namespace muduo;\n\nconst size_t N = 1000000;\n\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n\ntemplate<typename T>\nvoid benchPrintf(const char* fmt)\n{\n  char buf[32];\n  Timestamp start(Timestamp::now());\n  for (size_t i = 0; i < N; ++i)\n    snprintf(buf, sizeof buf, fmt, (T)(i));\n  Timestamp end(Timestamp::now());\n\n  printf(\"benchPrintf %f\\n\", timeDifference(end, start));\n}\n\ntemplate<typename T>\nvoid benchStringStream()\n{\n  Timestamp start(Timestamp::now());\n  std::ostringstream os;\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    os << (T)(i);\n    os.seekp(0, std::ios_base::beg);\n  }\n  Timestamp end(Timestamp::now());\n\n  printf(\"benchStringStream %f\\n\", timeDifference(end, start));\n}\n\ntemplate<typename T>\nvoid benchLogStream()\n{\n  Timestamp start(Timestamp::now());\n  LogStream os;\n  for (size_t i = 0; i < N; ++i)\n  {\n    os << (T)(i);\n    os.resetBuffer();\n  }\n  Timestamp end(Timestamp::now());\n\n  printf(\"benchLogStream %f\\n\", timeDifference(end, start));\n}\n\nint main()\n{\n  benchPrintf<int>(\"%d\");\n\n  puts(\"int\");\n  benchPrintf<int>(\"%d\");\n  benchStringStream<int>();\n  benchLogStream<int>();\n\n  puts(\"double\");\n  benchPrintf<double>(\"%.12g\");\n  benchStringStream<double>();\n  benchLogStream<double>();\n\n  puts(\"int64_t\");\n  benchPrintf<int64_t>(\"%\" PRId64);\n  benchStringStream<int64_t>();\n  benchLogStream<int64_t>();\n\n  puts(\"void*\");\n  benchPrintf<void*>(\"%p\");\n  benchStringStream<void*>();\n  benchLogStream<void*>();\n\n}\n"
  },
  {
    "path": "muduo/base/tests/LogStream_test.cc",
    "content": "#include \"muduo/base/LogStream.h\"\n\n#include <limits>\n#include <stdint.h>\n\n//#define BOOST_TEST_MODULE LogStreamTest\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\nusing muduo::string;\n\nBOOST_AUTO_TEST_CASE(testLogStreamBooleans)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"\"));\n  os << true;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1\"));\n  os << '\\n';\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1\\n\"));\n  os << false;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1\\n0\"));\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamIntegers)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"\"));\n  os << 1;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1\"));\n  os << 0;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"10\"));\n  os << -1;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"10-1\"));\n  os.resetBuffer();\n\n  os << 0 << \" \" << 123 << 'x' << 0x64;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0 123x100\"));\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamIntegerLimits)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n  os << -2147483647;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-2147483647\"));\n  os << static_cast<int>(-2147483647 - 1);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-2147483647-2147483648\"));\n  os << ' ';\n  os << 2147483647;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-2147483647-2147483648 2147483647\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int16_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-32768\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int16_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"32767\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint16_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint16_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"65535\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int32_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-2147483648\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int32_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"2147483647\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint32_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint32_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"4294967295\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int64_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-9223372036854775808\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<int64_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"9223372036854775807\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint64_t>::min();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0\"));\n  os.resetBuffer();\n\n  os << std::numeric_limits<uint64_t>::max();\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"18446744073709551615\"));\n  os.resetBuffer();\n\n  int16_t a = 0;\n  int32_t b = 0;\n  int64_t c = 0;\n  os << a;\n  os << b;\n  os << c;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"000\"));\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamFloats)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n\n  os << 0.0;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0\"));\n  os.resetBuffer();\n\n  os << 1.0;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1\"));\n  os.resetBuffer();\n\n  os << 0.1;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.1\"));\n  os.resetBuffer();\n\n  os << 0.05;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.05\"));\n  os.resetBuffer();\n\n  os << 0.15;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.15\"));\n  os.resetBuffer();\n\n  double a = 0.1;\n  os << a;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.1\"));\n  os.resetBuffer();\n\n  double b = 0.05;\n  os << b;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.05\"));\n  os.resetBuffer();\n\n  double c = 0.15;\n  os << c;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.15\"));\n  os.resetBuffer();\n\n  os << a+b;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0.15\"));\n  os.resetBuffer();\n\n  BOOST_CHECK(a+b != c);\n\n  os << 1.23456789;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1.23456789\"));\n  os.resetBuffer();\n\n  os << 1.234567;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1.234567\"));\n  os.resetBuffer();\n\n  os << -123.456;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"-123.456\"));\n  os.resetBuffer();\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamVoid)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n\n  os << static_cast<void*>(0);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0x0\"));\n  os.resetBuffer();\n\n  os << reinterpret_cast<void*>(8888);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"0x22B8\"));\n  os.resetBuffer();\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamStrings)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n\n  os << \"Hello \";\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"Hello \"));\n\n  string chenshuo = \"Shuo Chen\";\n  os << chenshuo;\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"Hello Shuo Chen\"));\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamFmts)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n\n  os << muduo::Fmt(\"%4d\", 1);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"   1\"));\n  os.resetBuffer();\n\n  os << muduo::Fmt(\"%4.2f\", 1.2);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1.20\"));\n  os.resetBuffer();\n\n  os << muduo::Fmt(\"%4.2f\", 1.2) << muduo::Fmt(\"%4d\", 43);\n  BOOST_CHECK_EQUAL(buf.toString(), string(\"1.20  43\"));\n  os.resetBuffer();\n}\n\nBOOST_AUTO_TEST_CASE(testLogStreamLong)\n{\n  muduo::LogStream os;\n  const muduo::LogStream::Buffer& buf = os.buffer();\n  for (int i = 0; i < 399; ++i)\n  {\n    os << \"123456789 \";\n    BOOST_CHECK_EQUAL(buf.length(), 10*(i+1));\n    BOOST_CHECK_EQUAL(buf.avail(), 4000 - 10*(i+1));\n  }\n\n  os << \"abcdefghi \";\n  BOOST_CHECK_EQUAL(buf.length(), 3990);\n  BOOST_CHECK_EQUAL(buf.avail(), 10);\n\n  os << \"abcdefghi\";\n  BOOST_CHECK_EQUAL(buf.length(), 3999);\n  BOOST_CHECK_EQUAL(buf.avail(), 1);\n}\n\nBOOST_AUTO_TEST_CASE(testFormatSI)\n{\n  BOOST_CHECK_EQUAL(muduo::formatSI(0), string(\"0\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(999), string(\"999\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(1000), string(\"1.00k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(9990), string(\"9.99k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(9994), string(\"9.99k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(9995), string(\"10.0k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(10000), string(\"10.0k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(10049), string(\"10.0k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(10050), string(\"10.1k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(99900), string(\"99.9k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(99949), string(\"99.9k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(99950), string(\"100k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(100499), string(\"100k\"));\n  // FIXME:\n  // BOOST_CHECK_EQUAL(muduo::formatSI(100500), string(\"101k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(100501), string(\"101k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(999499), string(\"999k\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(999500), string(\"1.00M\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(1004999), string(\"1.00M\"));\n  // BOOST_CHECK_EQUAL(muduo::formatSI(1005000), string(\"1.01M\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(1005001), string(\"1.01M\"));\n  BOOST_CHECK_EQUAL(muduo::formatSI(INT64_MAX), string(\"9.22E\"));\n}\n\nBOOST_AUTO_TEST_CASE(testFormatIEC)\n{\n  BOOST_CHECK_EQUAL(muduo::formatIEC(0), string(\"0\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1023), string(\"1023\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1024), string(\"1.00Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10234), string(\"9.99Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10235), string(\"10.0Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10240), string(\"10.0Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10291), string(\"10.0Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10292), string(\"10.1Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(102348), string(\"99.9Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(102349), string(\"100Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(102912), string(\"100Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(102913), string(\"101Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1022976), string(\"999Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1047552), string(\"1023Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1047961), string(\"1023Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1048063), string(\"1023Ki\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1048064), string(\"1.00Mi\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(1048576), string(\"1.00Mi\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10480517), string(\"9.99Mi\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(10480518), string(\"10.0Mi\"));\n  BOOST_CHECK_EQUAL(muduo::formatIEC(INT64_MAX), string(\"8.00Ei\"));\n}\n"
  },
  {
    "path": "muduo/base/tests/Logging_test.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/base/LogFile.h\"\n#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/base/TimeZone.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nint g_total;\nFILE* g_file;\nstd::unique_ptr<muduo::LogFile> g_logFile;\n\nvoid dummyOutput(const char* msg, int len)\n{\n  g_total += len;\n  if (g_file)\n  {\n    fwrite(msg, 1, len, g_file);\n  }\n  else if (g_logFile)\n  {\n    g_logFile->append(msg, len);\n  }\n}\n\nvoid bench(const char* type)\n{\n  muduo::Logger::setOutput(dummyOutput);\n  muduo::Timestamp start(muduo::Timestamp::now());\n  g_total = 0;\n\n  int n = 1000*1000;\n  const bool kLongLog = false;\n  muduo::string empty = \" \";\n  muduo::string longStr(3000, 'X');\n  longStr += \" \";\n  for (int i = 0; i < n; ++i)\n  {\n    LOG_INFO << \"Hello 0123456789\" << \" abcdefghijklmnopqrstuvwxyz\"\n             << (kLongLog ? longStr : empty)\n             << i;\n  }\n  muduo::Timestamp end(muduo::Timestamp::now());\n  double seconds = timeDifference(end, start);\n  printf(\"%12s: %f seconds, %d bytes, %10.2f msg/s, %.2f MiB/s\\n\",\n         type, seconds, g_total, n / seconds, g_total / seconds / (1024 * 1024));\n}\n\nvoid logInThread()\n{\n  LOG_INFO << \"logInThread\";\n  usleep(1000);\n}\n\nint main()\n{\n  getppid(); // for ltrace and strace\n\n  muduo::ThreadPool pool(\"pool\");\n  pool.start(5);\n  pool.run(logInThread);\n  pool.run(logInThread);\n  pool.run(logInThread);\n  pool.run(logInThread);\n  pool.run(logInThread);\n\n  LOG_TRACE << \"trace\";\n  LOG_DEBUG << \"debug\";\n  LOG_INFO << \"Hello\";\n  LOG_WARN << \"World\";\n  LOG_ERROR << \"Error\";\n  LOG_INFO << sizeof(muduo::Logger);\n  LOG_INFO << sizeof(muduo::LogStream);\n  LOG_INFO << sizeof(muduo::Fmt);\n  LOG_INFO << sizeof(muduo::LogStream::Buffer);\n\n  sleep(1);\n  bench(\"nop\");\n\n  char buffer[64*1024];\n\n  g_file = fopen(\"/dev/null\", \"w\");\n  setbuffer(g_file, buffer, sizeof buffer);\n  bench(\"/dev/null\");\n  fclose(g_file);\n\n  g_file = fopen(\"/tmp/log\", \"w\");\n  setbuffer(g_file, buffer, sizeof buffer);\n  bench(\"/tmp/log\");\n  fclose(g_file);\n\n  g_file = NULL;\n  g_logFile.reset(new muduo::LogFile(\"test_log_st\", 500*1000*1000, false));\n  bench(\"test_log_st\");\n\n  g_logFile.reset(new muduo::LogFile(\"test_log_mt\", 500*1000*1000, true));\n  bench(\"test_log_mt\");\n  g_logFile.reset();\n\n  {\n  g_file = stdout;\n  sleep(1);\n  muduo::TimeZone beijing(8*3600, \"CST\");\n  muduo::Logger::setTimeZone(beijing);\n  LOG_TRACE << \"trace CST\";\n  LOG_DEBUG << \"debug CST\";\n  LOG_INFO << \"Hello CST\";\n  LOG_WARN << \"World CST\";\n  LOG_ERROR << \"Error CST\";\n\n  sleep(1);\n  muduo::TimeZone newyork = muduo::TimeZone::loadZoneFile(\"/usr/share/zoneinfo/America/New_York\");\n  muduo::Logger::setTimeZone(newyork);\n  LOG_TRACE << \"trace NYT\";\n  LOG_DEBUG << \"debug NYT\";\n  LOG_INFO << \"Hello NYT\";\n  LOG_WARN << \"World NYT\";\n  LOG_ERROR << \"Error NYT\";\n  g_file = NULL;\n  }\n  bench(\"timezone nop\");\n}\n"
  },
  {
    "path": "muduo/base/tests/Mutex_test.cc",
    "content": "#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <vector>\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace std;\n\nMutexLock g_mutex;\nvector<int> g_vec;\nconst int kCount = 10*1000*1000;\n\nvoid threadFunc()\n{\n  for (int i = 0; i < kCount; ++i)\n  {\n    MutexLockGuard lock(g_mutex);\n    g_vec.push_back(i);\n  }\n}\n\nint foo() __attribute__ ((noinline));\n\nint g_count = 0;\nint foo()\n{\n  MutexLockGuard lock(g_mutex);\n  if (!g_mutex.isLockedByThisThread())\n  {\n    printf(\"FAIL\\n\");\n    return -1;\n  }\n\n  ++g_count;\n  return 0;\n}\n\nint main()\n{\n  printf(\"sizeof pthread_mutex_t: %zd\\n\", sizeof(pthread_mutex_t));\n  printf(\"sizeof Mutex: %zd\\n\", sizeof(MutexLock));\n  printf(\"sizeof pthread_cond_t: %zd\\n\", sizeof(pthread_cond_t));\n  printf(\"sizeof Condition: %zd\\n\", sizeof(Condition));\n  MCHECK(foo());\n  if (g_count != 1)\n  {\n    printf(\"MCHECK calls twice.\\n\");\n    abort();\n  }\n\n  const int kMaxThreads = 8;\n  g_vec.reserve(kMaxThreads * kCount);\n\n  Timestamp start(Timestamp::now());\n  for (int i = 0; i < kCount; ++i)\n  {\n    g_vec.push_back(i);\n  }\n\n  printf(\"single thread without lock %f\\n\", timeDifference(Timestamp::now(), start));\n\n  start = Timestamp::now();\n  threadFunc();\n  printf(\"single thread with lock %f\\n\", timeDifference(Timestamp::now(), start));\n\n  for (int nthreads = 1; nthreads < kMaxThreads; ++nthreads)\n  {\n    std::vector<std::unique_ptr<Thread>> threads;\n    g_vec.clear();\n    start = Timestamp::now();\n    for (int i = 0; i < nthreads; ++i)\n    {\n      threads.emplace_back(new Thread(&threadFunc));\n      threads.back()->start();\n    }\n    for (int i = 0; i < nthreads; ++i)\n    {\n      threads[i]->join();\n    }\n    printf(\"%d thread(s) with lock %f\\n\", nthreads, timeDifference(Timestamp::now(), start));\n  }\n}\n\n"
  },
  {
    "path": "muduo/base/tests/ProcessInfo_test.cc",
    "content": "#include \"muduo/base/ProcessInfo.h\"\n#include <stdio.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n\nint main()\n{\n  printf(\"pid = %d\\n\", muduo::ProcessInfo::pid());\n  printf(\"uid = %d\\n\", muduo::ProcessInfo::uid());\n  printf(\"euid = %d\\n\", muduo::ProcessInfo::euid());\n  printf(\"start time = %s\\n\", muduo::ProcessInfo::startTime().toFormattedString().c_str());\n  printf(\"hostname = %s\\n\", muduo::ProcessInfo::hostname().c_str());\n  printf(\"opened files = %d\\n\", muduo::ProcessInfo::openedFiles());\n  printf(\"threads = %zd\\n\", muduo::ProcessInfo::threads().size());\n  printf(\"num threads = %d\\n\", muduo::ProcessInfo::numThreads());\n  printf(\"status = %s\\n\", muduo::ProcessInfo::procStatus().c_str());\n}\n"
  },
  {
    "path": "muduo/base/tests/SingletonThreadLocal_test.cc",
    "content": "#include \"muduo/base/Singleton.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/ThreadLocal.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nclass Test : muduo::noncopyable\n{\n public:\n  Test()\n  {\n    printf(\"tid=%d, constructing %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n\n  ~Test()\n  {\n    printf(\"tid=%d, destructing %p %s\\n\", muduo::CurrentThread::tid(), this, name_.c_str());\n  }\n\n  const muduo::string& name() const { return name_; }\n  void setName(const muduo::string& n) { name_ = n; }\n\n private:\n  muduo::string name_;\n};\n\n#define STL muduo::Singleton<muduo::ThreadLocal<Test> >::instance().value()\n\nvoid print()\n{\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &STL,\n         STL.name().c_str());\n}\n\nvoid threadFunc(const char* changeTo)\n{\n  print();\n  STL.setName(changeTo);\n  sleep(1);\n  print();\n}\n\nint main()\n{\n  STL.setName(\"main one\");\n  muduo::Thread t1(std::bind(threadFunc, \"thread1\"));\n  muduo::Thread t2(std::bind(threadFunc, \"thread2\"));\n  t1.start();\n  t2.start();\n  t1.join();\n  print();\n  t2.join();\n  pthread_exit(0);\n}\n"
  },
  {
    "path": "muduo/base/tests/Singleton_test.cc",
    "content": "#include \"muduo/base/Singleton.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n\nclass Test : muduo::noncopyable\n{\n public:\n  Test()\n  {\n    printf(\"tid=%d, constructing %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n\n  ~Test()\n  {\n    printf(\"tid=%d, destructing %p %s\\n\", muduo::CurrentThread::tid(), this, name_.c_str());\n  }\n\n  const muduo::string& name() const { return name_; }\n  void setName(const muduo::string& n) { name_ = n; }\n\n private:\n  muduo::string name_;\n};\n\nclass TestNoDestroy : muduo::noncopyable\n{\n public:\n  // Tag member for Singleton<T>\n  void no_destroy();\n\n  TestNoDestroy()\n  {\n    printf(\"tid=%d, constructing TestNoDestroy %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n\n  ~TestNoDestroy()\n  {\n    printf(\"tid=%d, destructing TestNoDestroy %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n};\n\nvoid threadFunc()\n{\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &muduo::Singleton<Test>::instance(),\n         muduo::Singleton<Test>::instance().name().c_str());\n  muduo::Singleton<Test>::instance().setName(\"only one, changed\");\n}\n\nint main()\n{\n  muduo::Singleton<Test>::instance().setName(\"only one\");\n  muduo::Thread t1(threadFunc);\n  t1.start();\n  t1.join();\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &muduo::Singleton<Test>::instance(),\n         muduo::Singleton<Test>::instance().name().c_str());\n  muduo::Singleton<TestNoDestroy>::instance();\n  printf(\"with valgrind, you should see %zd-byte memory leak.\\n\", sizeof(TestNoDestroy));\n}\n"
  },
  {
    "path": "muduo/base/tests/ThreadLocalSingleton_test.cc",
    "content": "#include \"muduo/base/ThreadLocalSingleton.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n\nclass Test : muduo::noncopyable\n{\n public:\n  Test()\n  {\n    printf(\"tid=%d, constructing %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n\n  ~Test()\n  {\n    printf(\"tid=%d, destructing %p %s\\n\", muduo::CurrentThread::tid(), this, name_.c_str());\n  }\n\n  const muduo::string& name() const { return name_; }\n  void setName(const muduo::string& n) { name_ = n; }\n\n private:\n  muduo::string name_;\n};\n\nvoid threadFunc(const char* changeTo)\n{\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &muduo::ThreadLocalSingleton<Test>::instance(),\n         muduo::ThreadLocalSingleton<Test>::instance().name().c_str());\n  muduo::ThreadLocalSingleton<Test>::instance().setName(changeTo);\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &muduo::ThreadLocalSingleton<Test>::instance(),\n         muduo::ThreadLocalSingleton<Test>::instance().name().c_str());\n\n  // no need to manually delete it\n  // muduo::ThreadLocalSingleton<Test>::destroy();\n}\n\nint main()\n{\n  muduo::ThreadLocalSingleton<Test>::instance().setName(\"main one\");\n  muduo::Thread t1(std::bind(threadFunc, \"thread1\"));\n  muduo::Thread t2(std::bind(threadFunc, \"thread2\"));\n  t1.start();\n  t2.start();\n  t1.join();\n  printf(\"tid=%d, %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &muduo::ThreadLocalSingleton<Test>::instance(),\n         muduo::ThreadLocalSingleton<Test>::instance().name().c_str());\n  t2.join();\n\n  pthread_exit(0);\n}\n"
  },
  {
    "path": "muduo/base/tests/ThreadLocal_test.cc",
    "content": "#include \"muduo/base/ThreadLocal.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n\nclass Test : muduo::noncopyable\n{\n public:\n  Test()\n  {\n    printf(\"tid=%d, constructing %p\\n\", muduo::CurrentThread::tid(), this);\n  }\n\n  ~Test()\n  {\n    printf(\"tid=%d, destructing %p %s\\n\", muduo::CurrentThread::tid(), this, name_.c_str());\n  }\n\n  const muduo::string& name() const { return name_; }\n  void setName(const muduo::string& n) { name_ = n; }\n\n private:\n  muduo::string name_;\n};\n\nmuduo::ThreadLocal<Test> testObj1;\nmuduo::ThreadLocal<Test> testObj2;\n\nvoid print()\n{\n  printf(\"tid=%d, obj1 %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &testObj1.value(),\n         testObj1.value().name().c_str());\n  printf(\"tid=%d, obj2 %p name=%s\\n\",\n         muduo::CurrentThread::tid(),\n         &testObj2.value(),\n         testObj2.value().name().c_str());\n}\n\nvoid threadFunc()\n{\n  print();\n  testObj1.value().setName(\"changed 1\");\n  testObj2.value().setName(\"changed 42\");\n  print();\n}\n\nint main()\n{\n  testObj1.value().setName(\"main one\");\n  print();\n  muduo::Thread t1(threadFunc);\n  t1.start();\n  t1.join();\n  testObj2.value().setName(\"main two\");\n  print();\n\n  pthread_exit(0);\n}\n"
  },
  {
    "path": "muduo/base/tests/ThreadPool_test.cc",
    "content": "#include \"muduo/base/ThreadPool.h\"\n#include \"muduo/base/CountDownLatch.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <stdio.h>\n#include <unistd.h>  // usleep\n\nvoid print()\n{\n  printf(\"tid=%d\\n\", muduo::CurrentThread::tid());\n}\n\nvoid printString(const std::string& str)\n{\n  LOG_INFO << str;\n  usleep(100*1000);\n}\n\nvoid test(int maxSize)\n{\n  LOG_WARN << \"Test ThreadPool with max queue size = \" << maxSize;\n  muduo::ThreadPool pool(\"MainThreadPool\");\n  pool.setMaxQueueSize(maxSize);\n  pool.start(5);\n\n  LOG_WARN << \"Adding\";\n  pool.run(print);\n  pool.run(print);\n  for (int i = 0; i < 100; ++i)\n  {\n    char buf[32];\n    snprintf(buf, sizeof buf, \"task %d\", i);\n    pool.run(std::bind(printString, std::string(buf)));\n  }\n  LOG_WARN << \"Done\";\n\n  muduo::CountDownLatch latch(1);\n  pool.run(std::bind(&muduo::CountDownLatch::countDown, &latch));\n  latch.wait();\n  pool.stop();\n}\n\n/*\n * Wish we could do this in the future.\nvoid testMove()\n{\n  muduo::ThreadPool pool;\n  pool.start(2);\n\n  std::unique_ptr<int> x(new int(42));\n  pool.run([y = std::move(x)]{ printf(\"%d: %d\\n\", muduo::CurrentThread::tid(), *y); });\n  pool.stop();\n}\n*/\n\nvoid longTask(int num)\n{\n  LOG_INFO << \"longTask \" << num;\n  muduo::CurrentThread::sleepUsec(3000000);\n}\n\nvoid test2()\n{\n  LOG_WARN << \"Test ThreadPool by stoping early.\";\n  muduo::ThreadPool pool(\"ThreadPool\");\n  pool.setMaxQueueSize(5);\n  pool.start(3);\n\n  muduo::Thread thread1([&pool]()\n  {\n    for (int i = 0; i < 20; ++i)\n    {\n      pool.run(std::bind(longTask, i));\n    }\n  }, \"thread1\");\n  thread1.start();\n\n  muduo::CurrentThread::sleepUsec(5000000);\n  LOG_WARN << \"stop pool\";\n  pool.stop();  // early stop\n\n  thread1.join();\n  // run() after stop()\n  pool.run(print);\n  LOG_WARN << \"test2 Done\";\n}\n\nint main()\n{\n  test(0);\n  test(1);\n  test(5);\n  test(10);\n  test(50);\n  test2();\n}\n"
  },
  {
    "path": "muduo/base/tests/Thread_bench.cc",
    "content": "#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/BlockingQueue.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include <stdio.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\nbool g_verbose = false;\nmuduo::MutexLock g_mutex;\nmuduo::AtomicInt32 g_count;\nstd::map<int, int> g_delays;\n\nvoid threadFunc()\n{\n  //printf(\"tid=%d\\n\", muduo::CurrentThread::tid());\n  g_count.increment();\n}\n\nvoid threadFunc2(muduo::Timestamp start)\n{\n  muduo::Timestamp now(muduo::Timestamp::now());\n  int delay = static_cast<int>(timeDifference(now, start) * 1000000);\n  muduo::MutexLockGuard lock(g_mutex);\n  ++g_delays[delay];\n}\n\nvoid forkBench()\n{\n  sleep(10);\n  muduo::Timestamp start(muduo::Timestamp::now());\n  int kProcesses = 10*1000;\n\n  printf(\"Creating %d processes in serial\\n\", kProcesses);\n  for (int i = 0; i < kProcesses; ++i)\n  {\n    pid_t child = fork();\n    if (child == 0)\n    {\n      exit(0);\n    }\n    else\n    {\n      waitpid(child, NULL, 0);\n    }\n  }\n\n  double timeUsed = timeDifference(muduo::Timestamp::now(), start);\n  printf(\"time elapsed %.3f seconds, process creation time used %.3f us\\n\",\n        timeUsed, timeUsed*1e6/kProcesses);\n  printf(\"number of created processes %d\\n\", kProcesses);\n}\n\nclass Bench\n{\n public:\n  Bench(int numThreads)\n    : startLatch_(numThreads),\n      stopLatch_(1)\n  {\n    threads_.reserve(numThreads);\n    for (int i = 0; i < numThreads; ++i)\n    {\n      char name[32];\n      snprintf(name, sizeof name, \"work thread %d\", i);\n      threads_.emplace_back(new muduo::Thread(\n            [this] { threadFunc(); },\n            muduo::string(name)));\n    }\n  }\n\n  void Start()\n  {\n    const int numThreads = static_cast<int>(threads_.size());\n    printf(\"Creating %d threads in parallel\\n\", numThreads);\n    muduo::Timestamp start = muduo::Timestamp::now();\n\n    for (auto& thr : threads_)\n    {\n      thr->start();\n    }\n    startLatch_.wait();\n    double timeUsed = timeDifference(muduo::Timestamp::now(), start);\n    printf(\"all %d threads started, %.3fms total, %.3fus per thread\\n\",\n           numThreads, 1e3 * timeUsed, 1e6 * timeUsed / numThreads);\n\n    TimestampQueue::queue_type queue = start_.drain();\n    if (g_verbose)\n    {\n      // for (const auto& [tid, ts] : queue)\n      for (const auto& e : queue)\n      {\n        printf(\"thread %d, %.0f us\\n\", e.first, timeDifference(e.second, start) * 1e6);\n      }\n    }\n  }\n\n  void Stop()\n  {\n    muduo::Timestamp stop = muduo::Timestamp::now();\n    stopLatch_.countDown();\n    for (auto& thr : threads_)\n    {\n      thr->join();\n    }\n\n    muduo::Timestamp t2 = muduo::Timestamp::now();\n    printf(\"all %zd threads joined, %.3fms\\n\",\n           threads_.size(), 1e3 * timeDifference(t2, stop));\n    TimestampQueue::queue_type queue = done_.drain();\n    if (g_verbose)\n    {\n      // for (const auto& [tid, ts] : queue)\n      for (const auto& e : queue)\n      {\n        printf(\"thread %d, %.0f us\\n\", e.first, timeDifference(e.second, stop) * 1e6);\n      }\n    }\n  }\n\n private:\n  void threadFunc()\n  {\n    const int tid = muduo::CurrentThread::tid();\n    start_.put(std::make_pair(tid, muduo::Timestamp::now()));\n    startLatch_.countDown();\n    stopLatch_.wait();\n    done_.put(std::make_pair(tid, muduo::Timestamp::now()));\n  }\n\n  using TimestampQueue = muduo::BlockingQueue<std::pair<int, muduo::Timestamp>>;\n  TimestampQueue start_, run_, done_;\n  muduo::CountDownLatch startLatch_, stopLatch_;\n  std::vector<std::unique_ptr<muduo::Thread>> threads_;\n};\n\nint main(int argc, char* argv[])\n{\n  g_verbose = argc > 1;\n  printf(\"pid=%d, tid=%d, verbose=%d\\n\",\n         ::getpid(), muduo::CurrentThread::tid(), g_verbose);\n  muduo::Timestamp start(muduo::Timestamp::now());\n\n  int kThreads = 100*1000;\n  printf(\"Creating %d threads in serial\\n\", kThreads);\n  for (int i = 0; i < kThreads; ++i)\n  {\n    muduo::Thread t1(threadFunc);\n    t1.start();\n    t1.join();\n  }\n\n  double timeUsed = timeDifference(muduo::Timestamp::now(), start);\n  printf(\"elapsed %.3f seconds, thread creation time %.3f us\\n\", timeUsed,\n         timeUsed*1e6/kThreads);\n  printf(\"number of created threads %d, g_count = %d\\n\",\n         muduo::Thread::numCreated(), g_count.get());\n\n  for (int i = 0; i < kThreads; ++i)\n  {\n    muduo::Timestamp now(muduo::Timestamp::now());\n    muduo::Thread t2(std::bind(threadFunc2, now));\n    t2.start();\n    t2.join();\n  }\n\n  if (g_verbose)\n  {\n    muduo::MutexLockGuard lock(g_mutex);\n    for (const auto& delay : g_delays)\n    {\n      printf(\"delay = %d, count = %d\\n\",\n             delay.first, delay.second);\n    }\n  }\n\n  Bench t(10000);\n  t.Start();\n  t.Stop();\n\n  forkBench();\n}\n"
  },
  {
    "path": "muduo/base/tests/Thread_test.cc",
    "content": "#include \"muduo/base/Thread.h\"\n#include \"muduo/base/CurrentThread.h\"\n\n#include <string>\n#include <stdio.h>\n#include <unistd.h>\n\nvoid mysleep(int seconds)\n{\n  timespec t = { seconds, 0 };\n  nanosleep(&t, NULL);\n}\n\nvoid threadFunc()\n{\n  printf(\"tid=%d\\n\", muduo::CurrentThread::tid());\n}\n\nvoid threadFunc2(int x)\n{\n  printf(\"tid=%d, x=%d\\n\", muduo::CurrentThread::tid(), x);\n}\n\nvoid threadFunc3()\n{\n  printf(\"tid=%d\\n\", muduo::CurrentThread::tid());\n  mysleep(1);\n}\n\nclass Foo\n{\n public:\n  explicit Foo(double x)\n    : x_(x)\n  {\n  }\n\n  void memberFunc()\n  {\n    printf(\"tid=%d, Foo::x_=%f\\n\", muduo::CurrentThread::tid(), x_);\n  }\n\n  void memberFunc2(const std::string& text)\n  {\n    printf(\"tid=%d, Foo::x_=%f, text=%s\\n\", muduo::CurrentThread::tid(), x_, text.c_str());\n  }\n\n private:\n  double x_;\n};\n\nint main()\n{\n  printf(\"pid=%d, tid=%d\\n\", ::getpid(), muduo::CurrentThread::tid());\n\n  muduo::Thread t1(threadFunc);\n  t1.start();\n  printf(\"t1.tid=%d\\n\", t1.tid());\n  t1.join();\n\n  muduo::Thread t2(std::bind(threadFunc2, 42),\n                   \"thread for free function with argument\");\n  t2.start();\n  printf(\"t2.tid=%d\\n\", t2.tid());\n  t2.join();\n\n  Foo foo(87.53);\n  muduo::Thread t3(std::bind(&Foo::memberFunc, &foo),\n                   \"thread for member function without argument\");\n  t3.start();\n  t3.join();\n\n  muduo::Thread t4(std::bind(&Foo::memberFunc2, std::ref(foo), std::string(\"Shuo Chen\")));\n  t4.start();\n  t4.join();\n\n  {\n    muduo::Thread t5(threadFunc3);\n    t5.start();\n    // t5 may destruct eariler than thread creation.\n  }\n  mysleep(2);\n  {\n    muduo::Thread t6(threadFunc3);\n    t6.start();\n    mysleep(2);\n    // t6 destruct later than thread creation.\n  }\n  sleep(2);\n  printf(\"number of created threads %d\\n\", muduo::Thread::numCreated());\n}\n"
  },
  {
    "path": "muduo/base/tests/TimeZone_unittest.cc",
    "content": "#include \"muduo/base/TimeZone.h\"\n#include \"muduo/base/Types.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n\nusing muduo::DateTime;\nusing muduo::TimeZone;\n\nstruct tm getTm(int year, int month, int day,\n                int hour, int minute, int seconds)\n{\n  struct tm gmt;\n  muduo::memZero(&gmt, sizeof gmt);\n  gmt.tm_year = year - 1900;\n  gmt.tm_mon = month - 1;\n  gmt.tm_mday = day;\n  gmt.tm_hour = hour;\n  gmt.tm_min = minute;\n  gmt.tm_sec = seconds;\n  return gmt;\n}\n\nstruct tm getTm(const char* str)\n{\n  struct tm gmt;\n  muduo::memZero(&gmt, sizeof gmt);\n  strptime(str, \"%F %T\", &gmt);\n  return gmt;\n}\n\ntime_t getGmt(int year, int month, int day,\n              int hour, int minute, int seconds)\n{\n  struct tm gmt = getTm(year, month, day, hour, minute, seconds);\n  return timegm(&gmt);\n}\n\ntime_t getGmt(const char* str)\n{\n  struct tm gmt = getTm(str);\n  return timegm(&gmt);\n}\n\nstruct TestCase\n{\n  const char* gmt;\n  const char* local;\n  bool postTransition;\n};\n\nint failure = 0;\n\nvoid test(const TimeZone& tz, TestCase tc)\n{\n  const time_t gmt = getGmt(tc.gmt);\n\n  {\n  int utcOffset = 0;\n  std::string local = tz.toLocalTime(gmt, &utcOffset).toIsoString();\n  char buf[64];\n  snprintf(buf, sizeof buf, \" %+03d%02d\", utcOffset / 3600, utcOffset % 3600 / 60);\n  local += buf;\n\n  if (local != tc.local)\n  {\n    printf(\"WRONG: \");\n    printf(\"'%s' -> '%s' got '%s'\\n\", tc.gmt, tc.local, local.c_str());\n    failure++;\n  }\n  else\n  {\n    printf(\"'%s' -> '%s'\\n\", tc.gmt, local.c_str());\n  }\n  }\n\n  {\n  struct tm local = getTm(tc.local);\n  DateTime localtime(local.tm_year+1900, local.tm_mon+1, local.tm_mday,\n                     local.tm_hour, local.tm_min, local.tm_sec);\n  const int64_t result = tz.fromLocalTime(localtime, tc.postTransition);\n  if (result != gmt)\n  {\n    failure++;\n    printf(\"WRONG fromLocalTime: input %s expect %s got %s\\n\",\n           tc.local, tc.gmt, tz.toUtcTime(result).toIsoString().c_str());\n  }\n  }\n}\n\nvoid testNewYork()\n{\n  TimeZone tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/America/New_York\");\n  TestCase cases[] =\n  {\n\n    // Unix Epoch is 1969-12-31 local time.\n    { \"1970-01-01 00:00:00\", \"1969-12-31 19:00:00 -0500\", false },\n\n    { \"2006-03-07 00:00:00\", \"2006-03-06 19:00:00 -0500\", false },\n    { \"2006-04-02 06:59:59\", \"2006-04-02 01:59:59 -0500\", false },\n    { \"2006-04-02 07:00:00\", \"2006-04-02 03:00:00 -0400\", false },\n    { \"2006-05-01 00:00:00\", \"2006-04-30 20:00:00 -0400\", false },\n    { \"2006-05-02 01:00:00\", \"2006-05-01 21:00:00 -0400\", false },\n    { \"2006-10-21 05:00:00\", \"2006-10-21 01:00:00 -0400\", false },\n    { \"2006-10-29 05:59:59\", \"2006-10-29 01:59:59 -0400\", false },\n    { \"2006-10-29 06:00:00\", \"2006-10-29 01:00:00 -0500\", true },\n    { \"2006-10-29 06:30:00\", \"2006-10-29 01:30:00 -0500\", true },\n    { \"2006-12-31 06:00:00\", \"2006-12-31 01:00:00 -0500\", false },\n    { \"2007-01-01 00:00:00\", \"2006-12-31 19:00:00 -0500\", false },\n\n    { \"2007-03-07 00:00:00\", \"2007-03-06 19:00:00 -0500\", false },\n    { \"2007-03-11 06:59:59\", \"2007-03-11 01:59:59 -0500\", false },\n    { \"2007-03-11 07:00:00\", \"2007-03-11 03:00:00 -0400\", false },\n    { \"2007-05-01 00:00:00\", \"2007-04-30 20:00:00 -0400\", false },\n    { \"2007-05-02 01:00:00\", \"2007-05-01 21:00:00 -0400\", false },\n    { \"2007-10-31 05:00:00\", \"2007-10-31 01:00:00 -0400\", false },\n    { \"2007-11-04 05:59:59\", \"2007-11-04 01:59:59 -0400\", false },\n    { \"2007-11-04 06:00:00\", \"2007-11-04 01:00:00 -0500\", true },\n    { \"2007-11-04 06:59:59\", \"2007-11-04 01:59:59 -0500\", true },\n    { \"2007-12-31 06:00:00\", \"2007-12-31 01:00:00 -0500\", false },\n    { \"2008-01-01 00:00:00\", \"2007-12-31 19:00:00 -0500\", false },\n\n    { \"2009-03-07 00:00:00\", \"2009-03-06 19:00:00 -0500\", false },\n    { \"2009-03-08 06:59:59\", \"2009-03-08 01:59:59 -0500\", false },\n    { \"2009-03-08 07:00:00\", \"2009-03-08 03:00:00 -0400\", false },\n    { \"2009-05-01 00:00:00\", \"2009-04-30 20:00:00 -0400\", false },\n    { \"2009-05-02 01:00:00\", \"2009-05-01 21:00:00 -0400\", false },\n    { \"2009-10-31 05:00:00\", \"2009-10-31 01:00:00 -0400\", false },\n    { \"2009-11-01 05:59:59\", \"2009-11-01 01:59:59 -0400\", false },\n    { \"2009-11-01 06:00:00\", \"2009-11-01 01:00:00 -0500\", true },\n    { \"2009-11-01 06:59:59\", \"2009-11-01 01:59:59 -0500\", true },\n    { \"2009-12-31 06:00:00\", \"2009-12-31 01:00:00 -0500\", false },\n    { \"2010-01-01 00:00:00\", \"2009-12-31 19:00:00 -0500\", false },\n\n    { \"2010-03-13 00:00:00\", \"2010-03-12 19:00:00 -0500\", false },\n    { \"2010-03-14 06:59:59\", \"2010-03-14 01:59:59 -0500\", false },\n    { \"2010-03-14 07:00:00\", \"2010-03-14 03:00:00 -0400\", false },\n    { \"2010-05-01 00:00:00\", \"2010-04-30 20:00:00 -0400\", false },\n    { \"2010-05-02 01:00:00\", \"2010-05-01 21:00:00 -0400\", false },\n    { \"2010-11-06 05:00:00\", \"2010-11-06 01:00:00 -0400\", false },\n    { \"2010-11-07 05:59:59\", \"2010-11-07 01:59:59 -0400\", false },\n    { \"2010-11-07 06:00:00\", \"2010-11-07 01:00:00 -0500\", true },\n    { \"2010-11-07 06:59:59\", \"2010-11-07 01:59:59 -0500\", true },\n    { \"2010-12-31 06:00:00\", \"2010-12-31 01:00:00 -0500\", false },\n    { \"2011-01-01 00:00:00\", \"2010-12-31 19:00:00 -0500\", false },\n\n    { \"2011-03-01 00:00:00\", \"2011-02-28 19:00:00 -0500\", false },\n    { \"2011-03-13 06:59:59\", \"2011-03-13 01:59:59 -0500\", false },\n    { \"2011-03-13 07:00:00\", \"2011-03-13 03:00:00 -0400\", false },\n    { \"2011-05-01 00:00:00\", \"2011-04-30 20:00:00 -0400\", false },\n    { \"2011-05-02 01:00:00\", \"2011-05-01 21:00:00 -0400\", false },\n    { \"2011-11-06 05:59:59\", \"2011-11-06 01:59:59 -0400\", false },\n    { \"2011-11-06 06:00:00\", \"2011-11-06 01:00:00 -0500\", true },\n    { \"2011-11-06 06:59:59\", \"2011-11-06 01:59:59 -0500\", true },\n    { \"2011-12-31 06:00:00\", \"2011-12-31 01:00:00 -0500\", false },\n    { \"2012-01-01 00:00:00\", \"2011-12-31 19:00:00 -0500\", false },\n\n  };\n\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n}\n\nvoid testLondon()\n{\n  // UTC time             isdst  offset  Local time (London)\n  // 2010-03-28 01:00:00Z   1     1.0    2010-03-28 02:00:00\n  // 2010-10-31 01:00:00Z   0     0.0    2010-10-31 01:00:00\n  // 2011-03-27 01:00:00Z   1     1.0    2011-03-27 02:00:00\n  // 2011-10-30 01:00:00Z   0     0.0    2011-10-30 01:00:00\n  // 2012-03-25 01:00:00Z   1     1.0    2012-03-25 02:00:00\n  // 2012-10-28 01:00:00Z   0     0.0    2012-10-28 01:00:00\n  // 2013-03-31 01:00:00Z   1     1.0    2013-03-31 02:00:00\n  // 2013-10-27 01:00:00Z   0     0.0    2013-10-27 01:00:00\n  // 2014-03-30 01:00:00Z   1     1.0    2014-03-30 02:00:00\n  // 2014-10-26 01:00:00Z   0     0.0    2014-10-26 01:00:00\n\n  TimeZone tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/Europe/London\");\n  TestCase cases[] =\n  {\n\n    { \"2011-03-26 00:00:00\", \"2011-03-26 00:00:00 +0000\", false },\n    { \"2011-03-27 00:59:59\", \"2011-03-27 00:59:59 +0000\", false },\n    { \"2011-03-27 01:00:00\", \"2011-03-27 02:00:00 +0100\", false },\n    { \"2011-10-30 00:59:59\", \"2011-10-30 01:59:59 +0100\", false },\n    { \"2011-10-30 01:00:00\", \"2011-10-30 01:00:00 +0000\", true },\n    { \"2011-10-30 01:59:59\", \"2011-10-30 01:59:59 +0000\", true },\n    { \"2011-12-31 22:00:00\", \"2011-12-31 22:00:00 +0000\", false },\n    { \"2012-01-01 00:00:00\", \"2012-01-01 00:00:00 +0000\", false },\n\n    { \"2012-03-24 00:00:00\", \"2012-03-24 00:00:00 +0000\", false },\n    { \"2012-03-25 00:59:59\", \"2012-03-25 00:59:59 +0000\", false },\n    { \"2012-03-25 01:00:00\", \"2012-03-25 02:00:00 +0100\", false },\n    { \"2012-10-28 00:59:59\", \"2012-10-28 01:59:59 +0100\", false },\n    { \"2012-10-28 01:00:00\", \"2012-10-28 01:00:00 +0000\", true },\n    { \"2012-10-28 01:59:59\", \"2012-10-28 01:59:59 +0000\", true },\n    { \"2012-12-31 22:00:00\", \"2012-12-31 22:00:00 +0000\", false },\n    { \"2013-01-01 00:00:00\", \"2013-01-01 00:00:00 +0000\", false },\n\n  };\n\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n}\n\nvoid testHongKong()\n{\n  TimeZone tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/Asia/Hong_Kong\");\n  TestCase cases[] =\n  {\n\n    { \"2011-04-03 00:00:00\", \"2011-04-03 08:00:00 +0800\", false},\n\n  };\n\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n\n  tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/PRC\");\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n\n}\n\nvoid testSydney()\n{\n  // DST starts in winter\n  // UTC time             isdst  offset  Local time (London)\n  // 2010-04-03 16:00:00Z isdst 0 offset  10.0  2010-04-04 02:00:00\n  // 2010-10-02 16:00:00Z isdst 1 offset  11.0  2010-10-03 03:00:00\n  // 2011-04-02 16:00:00Z isdst 0 offset  10.0  2011-04-03 02:00:00\n  // 2011-10-01 16:00:00Z isdst 1 offset  11.0  2011-10-02 03:00:00\n  // 2012-03-31 16:00:00Z isdst 0 offset  10.0  2012-04-01 02:00:00\n  // 2012-10-06 16:00:00Z isdst 1 offset  11.0  2012-10-07 03:00:00\n\n  TimeZone tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/Australia/Sydney\");\n  TestCase cases[] =\n  {\n\n    { \"2011-01-01 00:00:00\", \"2011-01-01 11:00:00 +1100\", false },\n    { \"2011-04-02 15:59:59\", \"2011-04-03 02:59:59 +1100\", false },\n    { \"2011-04-02 16:00:00\", \"2011-04-03 02:00:00 +1000\", true },\n    { \"2011-04-02 16:59:59\", \"2011-04-03 02:59:59 +1000\", true },\n    { \"2011-05-02 01:00:00\", \"2011-05-02 11:00:00 +1000\", false },\n    { \"2011-10-01 15:59:59\", \"2011-10-02 01:59:59 +1000\", false },\n    { \"2011-10-01 16:00:00\", \"2011-10-02 03:00:00 +1100\", false },\n    { \"2011-12-31 22:00:00\", \"2012-01-01 09:00:00 +1100\", false },\n\n  };\n\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n}\n\nvoid testUtc()\n{\n  TimeZone utc = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/UTC\");\n  const int kRange = 100*1000*1000;\n  for (time_t t = -kRange; t <= kRange; t += 11)\n  {\n    struct tm* t1 = gmtime(&t);\n    char buf[80];\n    strftime(buf, sizeof buf, \"%F %T\", t1);\n\n    struct DateTime t2 = TimeZone::toUtcTime(t);\n    std::string t2str = t2.toIsoString();\n    if (t2str != buf)\n    {\n      printf(\"'%s' != '%s'\\n\", buf, t2str.c_str());\n      failure++;\n      assert(0);\n    }\n\n    struct DateTime t3 = utc.toLocalTime(t);\n    std::string t3str = t3.toIsoString();\n    if (t3str != buf)\n    {\n      printf(\"'%s' != '%s'\\n\", buf, t3str.c_str());\n      failure++;\n      assert(0);\n    }\n\n\n    int64_t u1 = TimeZone::fromUtcTime(t2);\n    if (t != u1)\n    {\n      printf(\"%lld != %lld\\n\", static_cast<long long>(t), static_cast<long long>(u1));\n      failure++;\n      assert(0);\n    }\n  }\n}\n\nvoid testFixedTimezone()\n{\n  TimeZone tz(8*3600, \"CST\");\n  TestCase cases[] =\n  {\n    { \"2014-04-03 00:00:00\", \"2014-04-03 08:00:00 +0800\", false},\n  };\n\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n\n  tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/Etc/GMT-8\");\n  for (const auto& c : cases)\n  {\n    test(tz, c);\n  }\n\n}\n\nstruct LocalToUtcTestCase\n{\n  const char* gmt;\n  const char* local;\n  bool postTransition;\n};\n\nvoid testLosAngeles()\n{\n  // UTC time             isdst  offset  Local time           Prior second local time\n  // 2021-03-14 10:00:00Z   1     -7.0   2021-03-14 03:00:00\n  // 2021-11-07 09:00:00Z   0     -8.0   2021-11-07 01:00:00\n  // 2022-03-13 10:00:00Z   1     -7.0   2022-03-13 03:00:00  2022-03-13 01:59:59\n  // 2022-11-06 09:00:00Z   0     -8.0   2022-11-06 01:00:00  2022-11-06 01:59:59\n  // 2023-03-12 10:00:00Z   1     -7.0   2023-03-12 03:00:00\n  // 2023-11-05 09:00:00Z   0     -8.0   2023-11-05 01:00:00\n\n  TimeZone tz = TimeZone::loadZoneFile(\"/usr/share/zoneinfo/America/Los_Angeles\");\n  int utcOffset = 0;\n  printf(\"1234567890 in Los Angeles: %s\", tz.toLocalTime(1234567890, &utcOffset).toIsoString().c_str());\n  printf(\" %+03d%02d\\n\", utcOffset / 3600, utcOffset % 3600 / 60);\n  printf(\"1666666666 in Los Angeles: %s\\n\", tz.toLocalTime(1666666666).toIsoString().c_str());\n  printf(\"Now in Los Angeles: %s\\n\", tz.toLocalTime(time(nullptr)).toIsoString().c_str());\n\n  LocalToUtcTestCase cases[] =\n  {\n    // Unix Epoch is 1969-12-31 local time.\n    {\"1970-01-01 00:00:00\", \"1969-12-31 16:00:00 -0800\", false },\n    {\"1970-01-01 00:00:00\", \"1969-12-31 16:00:00 -0800\", true },\n\n    {\"2022-01-01 18:00:00\", \"2022-01-01 10:00:00\", false},\n    {\"2022-01-01 18:00:00\", \"2022-01-01 10:00:00\", true },\n    // Before DST\n    {\"2022-03-13 09:00:00\", \"2022-03-13 01:00:00\", false },\n    {\"2022-03-13 09:00:00\", \"2022-03-13 01:00:00\", true },\n    {\"2022-03-13 09:59:59\", \"2022-03-13 01:59:59\", false },\n    {\"2022-03-13 09:59:59\", \"2022-03-13 01:59:59\", true },\n    // local time doesn't exist, skipped\n    {\"2022-03-13 10:00:00\", \"2022-03-13 02:00:00\", false },\n    {\"2022-03-13 09:00:00\", \"2022-03-13 02:00:00\", true },\n    {\"2022-03-13 10:59:59\", \"2022-03-13 02:59:59\", false },\n    {\"2022-03-13 09:59:59\", \"2022-03-13 02:59:59\", true },\n    // in DST\n    {\"2022-03-13 10:00:00\", \"2022-03-13 03:00:00\", false },\n    {\"2022-03-13 10:00:00\", \"2022-03-13 03:00:00\", true },\n    // Before back to winter time\n    {\"2022-11-06 07:59:59\", \"2022-11-06 00:59:59\", false },\n    {\"2022-11-06 07:59:59\", \"2022-11-06 00:59:59\", true },\n    // Time repeats\n    {\"2022-11-06 08:00:00\", \"2022-11-06 01:00:00\", false },\n    {\"2022-11-06 09:00:00\", \"2022-11-06 01:00:00\", true },\n    {\"2022-11-06 08:59:59\", \"2022-11-06 01:59:59\", false },\n    {\"2022-11-06 09:59:59\", \"2022-11-06 01:59:59\", true },\n    // After DST\n    {\"2022-11-06 10:00:00\", \"2022-11-06 02:00:00\", false },\n    {\"2022-11-06 10:00:00\", \"2022-11-06 02:00:00\", true },\n    {\"2023-01-01 06:00:00\", \"2022-12-31 22:00:00\", false },\n    {\"2023-01-01 06:00:00\", \"2022-12-31 22:00:00\", true },\n  };\n\n  for (const auto& tc : cases)\n  {\n    int64_t gmt = getGmt(tc.gmt);\n    struct tm local = getTm(tc.local);\n    DateTime localtime(local.tm_year+1900, local.tm_mon+1, local.tm_mday,\n                       local.tm_hour, local.tm_min, local.tm_sec);\n    int64_t actual = tz.fromLocalTime(localtime, tc.postTransition);\n    printf(\"Local %s -> %sZ\\n\", tc.local, tc.gmt);\n    if (gmt != actual)\n    {\n      failure++;\n      printf(\"WRONG: input %s post %d expect %s got %s\\n\",\n             tc.local, tc.postTransition, tc.gmt,\n             tz.toUtcTime(actual).toIsoString().c_str());\n    }\n  }\n\n  int64_t start = getGmt(\"1950-01-01 00:00:00\");\n  int64_t end = getGmt(\"2037-01-01 00:00:00\");\n  for (int64_t utc = start; utc <= end; utc += 30)\n  {\n    DateTime local = tz.toLocalTime(utc);\n    int64_t gmt = tz.fromLocalTime(local);\n    if (utc != gmt)\n    {\n      // try post transistion\n      int64_t post = tz.fromLocalTime(local, true);\n      if (post != utc)\n      {\n        failure++;\n        printf(\"WRONG: input %s local %s got %s\\n\",\n               tz.toUtcTime(utc).toIsoString().c_str(),\n               local.toIsoString().c_str(),\n               tz.toUtcTime(gmt).toIsoString().c_str());\n      }\n    }\n  }\n}\n\nint main()\n{\n  testLosAngeles();\n  testNewYork();\n  testLondon();\n  testSydney();\n  testHongKong();\n  testFixedTimezone();\n  testUtc();\n\n  return failure;\n}\n"
  },
  {
    "path": "muduo/base/tests/TimeZone_util.cc",
    "content": "#include \"muduo/base/TimeZone.h\"\n\n#include <assert.h>\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <inttypes.h>\n\n#include <string>\n\nusing muduo::DateTime;\nusing muduo::TimeZone;\n\nvoid printUtcAndLocal(int64_t utc, TimeZone local)\n{\n  printf(\"Unix Time: %\" PRId64 \"\\n\", utc);\n  printf(\"UTC:       %s\\n\", TimeZone::toUtcTime(utc).toIsoString().c_str());\n  int utcOffset = 0;\n  printf(\"Local:     %s\", local.toLocalTime(utc, &utcOffset).toIsoString().c_str());\n  printf(\" %+03d%02d\\n\", utcOffset / 3600, utcOffset % 3600 / 60);\n}\n\nint main(int argc, char* argv[])\n{\n  TimeZone local = TimeZone::loadZoneFile(\"/etc/localtime\");\n  if (argc <= 1)\n  {\n    time_t now = ::time(NULL);\n    printUtcAndLocal(now, local);\n    return 0;\n  }\n\n  // TODO: input is from a different timezone.\n\n  for (int i = 1; i < argc; ++i)\n  {\n    char* end = NULL;\n    int64_t t = strtol(argv[i], &end, 10);\n    if (end > argv[i] && *end == '\\0')\n    {\n      printUtcAndLocal(t, local);\n    }\n    else\n    {\n      struct tm tm = { };\n      end = strptime(argv[i], \"%F %T\", &tm);\n      if (end != NULL && *end == '\\0')\n      {\n        DateTime dt(tm);\n        t = local.fromLocalTime(dt);\n        printUtcAndLocal(t, local);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "muduo/base/tests/Timestamp_unittest.cc",
    "content": "#include \"muduo/base/Timestamp.h\"\n#include <vector>\n#include <stdio.h>\n\nusing muduo::Timestamp;\n\nvoid passByConstReference(const Timestamp& x)\n{\n  printf(\"%s\\n\", x.toString().c_str());\n}\n\nvoid passByValue(Timestamp x)\n{\n  printf(\"%s\\n\", x.toString().c_str());\n}\n\nvoid benchmark()\n{\n  const int kNumber = 1000*1000;\n\n  std::vector<Timestamp> stamps;\n  stamps.reserve(kNumber);\n  for (int i = 0; i < kNumber; ++i)\n  {\n    stamps.push_back(Timestamp::now());\n  }\n  printf(\"%s\\n\", stamps.front().toString().c_str());\n  printf(\"%s\\n\", stamps.back().toString().c_str());\n  printf(\"%f\\n\", timeDifference(stamps.back(), stamps.front()));\n\n  int increments[100] = { 0 };\n  int64_t start = stamps.front().microSecondsSinceEpoch();\n  for (int i = 1; i < kNumber; ++i)\n  {\n    int64_t next = stamps[i].microSecondsSinceEpoch();\n    int64_t inc = next - start;\n    start = next;\n    if (inc < 0)\n    {\n      printf(\"reverse!\\n\");\n    }\n    else if (inc < 100)\n    {\n      ++increments[inc];\n    }\n    else\n    {\n      printf(\"big gap %d\\n\", static_cast<int>(inc));\n    }\n  }\n\n  for (int i = 0; i < 100; ++i)\n  {\n    printf(\"%2d: %d\\n\", i, increments[i]);\n  }\n}\n\nint main()\n{\n  Timestamp now(Timestamp::now());\n  printf(\"%s\\n\", now.toString().c_str());\n  passByValue(now);\n  passByConstReference(now);\n  benchmark();\n}\n\n"
  },
  {
    "path": "muduo/net/Acceptor.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/Acceptor.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n//#include <sys/types.h>\n//#include <sys/stat.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nAcceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)\n  : loop_(loop),\n    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),\n    acceptChannel_(loop, acceptSocket_.fd()),\n    listening_(false),\n    idleFd_(::open(\"/dev/null\", O_RDONLY | O_CLOEXEC))\n{\n  assert(idleFd_ >= 0);\n  acceptSocket_.setReuseAddr(true);\n  acceptSocket_.setReusePort(reuseport);\n  acceptSocket_.bindAddress(listenAddr);\n  acceptChannel_.setReadCallback(\n      std::bind(&Acceptor::handleRead, this));\n}\n\nAcceptor::~Acceptor()\n{\n  acceptChannel_.disableAll();\n  acceptChannel_.remove();\n  ::close(idleFd_);\n}\n\nvoid Acceptor::listen()\n{\n  loop_->assertInLoopThread();\n  listening_ = true;\n  acceptSocket_.listen();\n  acceptChannel_.enableReading();\n}\n\nvoid Acceptor::handleRead()\n{\n  loop_->assertInLoopThread();\n  InetAddress peerAddr;\n  //FIXME loop until no more\n  int connfd = acceptSocket_.accept(&peerAddr);\n  if (connfd >= 0)\n  {\n    // string hostport = peerAddr.toIpPort();\n    // LOG_TRACE << \"Accepts of \" << hostport;\n    if (newConnectionCallback_)\n    {\n      newConnectionCallback_(connfd, peerAddr);\n    }\n    else\n    {\n      sockets::close(connfd);\n    }\n  }\n  else\n  {\n    LOG_SYSERR << \"in Acceptor::handleRead\";\n    // Read the section named \"The special problem of\n    // accept()ing when you can't\" in libev's doc.\n    // By Marc Lehmann, author of libev.\n    if (errno == EMFILE)\n    {\n      ::close(idleFd_);\n      idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);\n      ::close(idleFd_);\n      idleFd_ = ::open(\"/dev/null\", O_RDONLY | O_CLOEXEC);\n    }\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/Acceptor.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_ACCEPTOR_H\n#define MUDUO_NET_ACCEPTOR_H\n\n#include <functional>\n\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/Socket.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass EventLoop;\nclass InetAddress;\n\n///\n/// Acceptor of incoming TCP connections.\n///\nclass Acceptor : noncopyable\n{\n public:\n  typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;\n\n  Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);\n  ~Acceptor();\n\n  void setNewConnectionCallback(const NewConnectionCallback& cb)\n  { newConnectionCallback_ = cb; }\n\n  void listen();\n\n  bool listening() const { return listening_; }\n\n  // Deprecated, use the correct spelling one above.\n  // Leave the wrong spelling here in case one needs to grep it for error messages.\n  // bool listenning() const { return listening(); }\n\n private:\n  void handleRead();\n\n  EventLoop* loop_;\n  Socket acceptSocket_;\n  Channel acceptChannel_;\n  NewConnectionCallback newConnectionCallback_;\n  bool listening_;\n  int idleFd_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_ACCEPTOR_H\n"
  },
  {
    "path": "muduo/net/BUILD.bazel",
    "content": "cc_library(\n    name = \"net\",\n    srcs = [\n        \"Acceptor.cc\",\n        \"Buffer.cc\",\n        \"Channel.cc\",\n        \"Connector.cc\",\n        \"EventLoop.cc\",\n        \"EventLoopThread.cc\",\n        \"EventLoopThreadPool.cc\",\n        \"InetAddress.cc\",\n        \"Poller.cc\",\n        \"Socket.cc\",\n        \"SocketsOps.cc\",\n        \"TcpClient.cc\",\n        \"TcpConnection.cc\",\n        \"TcpServer.cc\",\n        \"Timer.cc\",\n        \"TimerQueue.cc\",\n        \"poller/DefaultPoller.cc\",\n        \"poller/EPollPoller.cc\",\n        \"poller/PollPoller.cc\",\n    ],\n    hdrs = [\n        \"Acceptor.h\",\n        \"Buffer.h\",\n        \"Callbacks.h\",\n        \"Channel.h\",\n        \"Connector.h\",\n        \"Endian.h\",\n        \"EventLoop.h\",\n        \"EventLoopThread.h\",\n        \"EventLoopThreadPool.h\",\n        \"InetAddress.h\",\n        \"Poller.h\",\n        \"Socket.h\",\n        \"SocketsOps.h\",\n        \"TcpClient.h\",\n        \"TcpConnection.h\",\n        \"TcpServer.h\",\n        \"Timer.h\",\n        \"TimerId.h\",\n        \"TimerQueue.h\",\n        \"poller/EPollPoller.h\",\n        \"poller/PollPoller.h\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//muduo/base\",\n    ],\n)\n"
  },
  {
    "path": "muduo/net/Buffer.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/Buffer.h\"\n\n#include \"muduo/net/SocketsOps.h\"\n\n#include <errno.h>\n#include <sys/uio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst char Buffer::kCRLF[] = \"\\r\\n\";\n\nconst size_t Buffer::kCheapPrepend;\nconst size_t Buffer::kInitialSize;\n\nssize_t Buffer::readFd(int fd, int* savedErrno)\n{\n  // saved an ioctl()/FIONREAD call to tell how much to read\n  char extrabuf[65536];\n  struct iovec vec[2];\n  const size_t writable = writableBytes();\n  vec[0].iov_base = begin()+writerIndex_;\n  vec[0].iov_len = writable;\n  vec[1].iov_base = extrabuf;\n  vec[1].iov_len = sizeof extrabuf;\n  // when there is enough space in this buffer, don't read into extrabuf.\n  // when extrabuf is used, we read 128k-1 bytes at most.\n  const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;\n  const ssize_t n = sockets::readv(fd, vec, iovcnt);\n  if (n < 0)\n  {\n    *savedErrno = errno;\n  }\n  else if (implicit_cast<size_t>(n) <= writable)\n  {\n    writerIndex_ += n;\n  }\n  else\n  {\n    writerIndex_ = buffer_.size();\n    append(extrabuf, n - writable);\n  }\n  // if (n == writable + sizeof extrabuf)\n  // {\n  //   goto line_30;\n  // }\n  return n;\n}\n\n"
  },
  {
    "path": "muduo/net/Buffer.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_BUFFER_H\n#define MUDUO_NET_BUFFER_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n\n#include \"muduo/net/Endian.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include <assert.h>\n#include <string.h>\n//#include <unistd.h>  // ssize_t\n\nnamespace muduo\n{\nnamespace net\n{\n\n/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer\n///\n/// @code\n/// +-------------------+------------------+------------------+\n/// | prependable bytes |  readable bytes  |  writable bytes  |\n/// |                   |     (CONTENT)    |                  |\n/// +-------------------+------------------+------------------+\n/// |                   |                  |                  |\n/// 0      <=      readerIndex   <=   writerIndex    <=     size\n/// @endcode\nclass Buffer : public muduo::copyable\n{\n public:\n  static const size_t kCheapPrepend = 8;\n  static const size_t kInitialSize = 1024;\n\n  explicit Buffer(size_t initialSize = kInitialSize)\n    : buffer_(kCheapPrepend + initialSize),\n      readerIndex_(kCheapPrepend),\n      writerIndex_(kCheapPrepend)\n  {\n    assert(readableBytes() == 0);\n    assert(writableBytes() == initialSize);\n    assert(prependableBytes() == kCheapPrepend);\n  }\n\n  // implicit copy-ctor, move-ctor, dtor and assignment are fine\n  // NOTE: implicit move-ctor is added in g++ 4.6\n\n  void swap(Buffer& rhs)\n  {\n    buffer_.swap(rhs.buffer_);\n    std::swap(readerIndex_, rhs.readerIndex_);\n    std::swap(writerIndex_, rhs.writerIndex_);\n  }\n\n  size_t readableBytes() const\n  { return writerIndex_ - readerIndex_; }\n\n  size_t writableBytes() const\n  { return buffer_.size() - writerIndex_; }\n\n  size_t prependableBytes() const\n  { return readerIndex_; }\n\n  const char* peek() const\n  { return begin() + readerIndex_; }\n\n  const char* findCRLF() const\n  {\n    // FIXME: replace with memmem()?\n    const char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF+2);\n    return crlf == beginWrite() ? NULL : crlf;\n  }\n\n  const char* findCRLF(const char* start) const\n  {\n    assert(peek() <= start);\n    assert(start <= beginWrite());\n    // FIXME: replace with memmem()?\n    const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);\n    return crlf == beginWrite() ? NULL : crlf;\n  }\n\n  const char* findEOL() const\n  {\n    const void* eol = memchr(peek(), '\\n', readableBytes());\n    return static_cast<const char*>(eol);\n  }\n\n  const char* findEOL(const char* start) const\n  {\n    assert(peek() <= start);\n    assert(start <= beginWrite());\n    const void* eol = memchr(start, '\\n', beginWrite() - start);\n    return static_cast<const char*>(eol);\n  }\n\n  // retrieve returns void, to prevent\n  // string str(retrieve(readableBytes()), readableBytes());\n  // the evaluation of two functions are unspecified\n  void retrieve(size_t len)\n  {\n    assert(len <= readableBytes());\n    if (len < readableBytes())\n    {\n      readerIndex_ += len;\n    }\n    else\n    {\n      retrieveAll();\n    }\n  }\n\n  void retrieveUntil(const char* end)\n  {\n    assert(peek() <= end);\n    assert(end <= beginWrite());\n    retrieve(end - peek());\n  }\n\n  void retrieveInt64()\n  {\n    retrieve(sizeof(int64_t));\n  }\n\n  void retrieveInt32()\n  {\n    retrieve(sizeof(int32_t));\n  }\n\n  void retrieveInt16()\n  {\n    retrieve(sizeof(int16_t));\n  }\n\n  void retrieveInt8()\n  {\n    retrieve(sizeof(int8_t));\n  }\n\n  void retrieveAll()\n  {\n    readerIndex_ = kCheapPrepend;\n    writerIndex_ = kCheapPrepend;\n  }\n\n  string retrieveAllAsString()\n  {\n    return retrieveAsString(readableBytes());\n  }\n\n  string retrieveAsString(size_t len)\n  {\n    assert(len <= readableBytes());\n    string result(peek(), len);\n    retrieve(len);\n    return result;\n  }\n\n  StringPiece toStringPiece() const\n  {\n    return StringPiece(peek(), static_cast<int>(readableBytes()));\n  }\n\n  void append(const StringPiece& str)\n  {\n    append(str.data(), str.size());\n  }\n\n  void append(const char* /*restrict*/ data, size_t len)\n  {\n    ensureWritableBytes(len);\n    std::copy(data, data+len, beginWrite());\n    hasWritten(len);\n  }\n\n  void append(const void* /*restrict*/ data, size_t len)\n  {\n    append(static_cast<const char*>(data), len);\n  }\n\n  void ensureWritableBytes(size_t len)\n  {\n    if (writableBytes() < len)\n    {\n      makeSpace(len);\n    }\n    assert(writableBytes() >= len);\n  }\n\n  char* beginWrite()\n  { return begin() + writerIndex_; }\n\n  const char* beginWrite() const\n  { return begin() + writerIndex_; }\n\n  void hasWritten(size_t len)\n  {\n    assert(len <= writableBytes());\n    writerIndex_ += len;\n  }\n\n  void unwrite(size_t len)\n  {\n    assert(len <= readableBytes());\n    writerIndex_ -= len;\n  }\n\n  ///\n  /// Append int64_t using network endian\n  ///\n  void appendInt64(int64_t x)\n  {\n    int64_t be64 = sockets::hostToNetwork64(x);\n    append(&be64, sizeof be64);\n  }\n\n  ///\n  /// Append int32_t using network endian\n  ///\n  void appendInt32(int32_t x)\n  {\n    int32_t be32 = sockets::hostToNetwork32(x);\n    append(&be32, sizeof be32);\n  }\n\n  void appendInt16(int16_t x)\n  {\n    int16_t be16 = sockets::hostToNetwork16(x);\n    append(&be16, sizeof be16);\n  }\n\n  void appendInt8(int8_t x)\n  {\n    append(&x, sizeof x);\n  }\n\n  ///\n  /// Read int64_t from network endian\n  ///\n  /// Require: buf->readableBytes() >= sizeof(int32_t)\n  int64_t readInt64()\n  {\n    int64_t result = peekInt64();\n    retrieveInt64();\n    return result;\n  }\n\n  ///\n  /// Read int32_t from network endian\n  ///\n  /// Require: buf->readableBytes() >= sizeof(int32_t)\n  int32_t readInt32()\n  {\n    int32_t result = peekInt32();\n    retrieveInt32();\n    return result;\n  }\n\n  int16_t readInt16()\n  {\n    int16_t result = peekInt16();\n    retrieveInt16();\n    return result;\n  }\n\n  int8_t readInt8()\n  {\n    int8_t result = peekInt8();\n    retrieveInt8();\n    return result;\n  }\n\n  ///\n  /// Peek int64_t from network endian\n  ///\n  /// Require: buf->readableBytes() >= sizeof(int64_t)\n  int64_t peekInt64() const\n  {\n    assert(readableBytes() >= sizeof(int64_t));\n    int64_t be64 = 0;\n    ::memcpy(&be64, peek(), sizeof be64);\n    return sockets::networkToHost64(be64);\n  }\n\n  ///\n  /// Peek int32_t from network endian\n  ///\n  /// Require: buf->readableBytes() >= sizeof(int32_t)\n  int32_t peekInt32() const\n  {\n    assert(readableBytes() >= sizeof(int32_t));\n    int32_t be32 = 0;\n    ::memcpy(&be32, peek(), sizeof be32);\n    return sockets::networkToHost32(be32);\n  }\n\n  int16_t peekInt16() const\n  {\n    assert(readableBytes() >= sizeof(int16_t));\n    int16_t be16 = 0;\n    ::memcpy(&be16, peek(), sizeof be16);\n    return sockets::networkToHost16(be16);\n  }\n\n  int8_t peekInt8() const\n  {\n    assert(readableBytes() >= sizeof(int8_t));\n    int8_t x = *peek();\n    return x;\n  }\n\n  ///\n  /// Prepend int64_t using network endian\n  ///\n  void prependInt64(int64_t x)\n  {\n    int64_t be64 = sockets::hostToNetwork64(x);\n    prepend(&be64, sizeof be64);\n  }\n\n  ///\n  /// Prepend int32_t using network endian\n  ///\n  void prependInt32(int32_t x)\n  {\n    int32_t be32 = sockets::hostToNetwork32(x);\n    prepend(&be32, sizeof be32);\n  }\n\n  void prependInt16(int16_t x)\n  {\n    int16_t be16 = sockets::hostToNetwork16(x);\n    prepend(&be16, sizeof be16);\n  }\n\n  void prependInt8(int8_t x)\n  {\n    prepend(&x, sizeof x);\n  }\n\n  void prepend(const void* /*restrict*/ data, size_t len)\n  {\n    assert(len <= prependableBytes());\n    readerIndex_ -= len;\n    const char* d = static_cast<const char*>(data);\n    std::copy(d, d+len, begin()+readerIndex_);\n  }\n\n  void shrink(size_t reserve)\n  {\n    // FIXME: use vector::shrink_to_fit() in C++ 11 if possible.\n    Buffer other;\n    other.ensureWritableBytes(readableBytes()+reserve);\n    other.append(toStringPiece());\n    swap(other);\n  }\n\n  size_t internalCapacity() const\n  {\n    return buffer_.capacity();\n  }\n\n  /// Read data directly into buffer.\n  ///\n  /// It may implement with readv(2)\n  /// @return result of read(2), @c errno is saved\n  ssize_t readFd(int fd, int* savedErrno);\n\n private:\n\n  char* begin()\n  { return &*buffer_.begin(); }\n\n  const char* begin() const\n  { return &*buffer_.begin(); }\n\n  void makeSpace(size_t len)\n  {\n    if (writableBytes() + prependableBytes() < len + kCheapPrepend)\n    {\n      // FIXME: move readable data\n      buffer_.resize(writerIndex_+len);\n    }\n    else\n    {\n      // move readable data to the front, make space inside buffer\n      assert(kCheapPrepend < readerIndex_);\n      size_t readable = readableBytes();\n      std::copy(begin()+readerIndex_,\n                begin()+writerIndex_,\n                begin()+kCheapPrepend);\n      readerIndex_ = kCheapPrepend;\n      writerIndex_ = readerIndex_ + readable;\n      assert(readable == readableBytes());\n    }\n  }\n\n private:\n  std::vector<char> buffer_;\n  size_t readerIndex_;\n  size_t writerIndex_;\n\n  static const char kCRLF[];\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_BUFFER_H\n"
  },
  {
    "path": "muduo/net/CMakeLists.txt",
    "content": "include(CheckFunctionExists)\n\ncheck_function_exists(accept4 HAVE_ACCEPT4)\nif(NOT HAVE_ACCEPT4)\n  set_source_files_properties(SocketsOps.cc PROPERTIES COMPILE_FLAGS \"-DNO_ACCEPT4\")\nendif()\n\nset(net_SRCS\n  Acceptor.cc\n  Buffer.cc\n  Channel.cc\n  Connector.cc\n  EventLoop.cc\n  EventLoopThread.cc\n  EventLoopThreadPool.cc\n  InetAddress.cc\n  Poller.cc\n  poller/DefaultPoller.cc\n  poller/EPollPoller.cc\n  poller/PollPoller.cc\n  Socket.cc\n  SocketsOps.cc\n  TcpClient.cc\n  TcpConnection.cc\n  TcpServer.cc\n  Timer.cc\n  TimerQueue.cc\n  )\n\nadd_library(muduo_net ${net_SRCS})\ntarget_link_libraries(muduo_net muduo_base)\n\n#add_library(muduo_net_cpp11 ${net_SRCS})\n#target_link_libraries(muduo_net_cpp11 muduo_base_cpp11)\n#set_target_properties(muduo_net_cpp11 PROPERTIES COMPILE_FLAGS \"-std=c++0x\")\n\ninstall(TARGETS muduo_net DESTINATION lib)\n#install(TARGETS muduo_net_cpp11 DESTINATION lib)\n\nset(HEADERS\n  Buffer.h\n  Callbacks.h\n  Channel.h\n  Endian.h\n  EventLoop.h\n  EventLoopThread.h\n  EventLoopThreadPool.h\n  InetAddress.h\n  TcpClient.h\n  TcpConnection.h\n  TcpServer.h\n  TimerId.h\n  )\ninstall(FILES ${HEADERS} DESTINATION include/muduo/net)\n\nadd_subdirectory(http)\nadd_subdirectory(inspect)\n\nif(MUDUO_BUILD_EXAMPLES)\n  add_subdirectory(tests)\nendif()\n\nif(PROTOBUF_FOUND)\n  add_subdirectory(protobuf)\n  add_subdirectory(protorpc)\nelse()\n  add_subdirectory(protobuf EXCLUDE_FROM_ALL)\n  add_subdirectory(protorpc EXCLUDE_FROM_ALL)\nendif()\n"
  },
  {
    "path": "muduo/net/Callbacks.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_CALLBACKS_H\n#define MUDUO_NET_CALLBACKS_H\n\n#include \"muduo/base/Timestamp.h\"\n\n#include <functional>\n#include <memory>\n\nnamespace muduo\n{\n\nusing std::placeholders::_1;\nusing std::placeholders::_2;\nusing std::placeholders::_3;\n\n// should really belong to base/Types.h, but <memory> is not included there.\n\ntemplate<typename T>\ninline T* get_pointer(const std::shared_ptr<T>& ptr)\n{\n  return ptr.get();\n}\n\ntemplate<typename T>\ninline T* get_pointer(const std::unique_ptr<T>& ptr)\n{\n  return ptr.get();\n}\n\n// Adapted from google-protobuf stubs/common.h\n// see License in muduo/base/Types.h\ntemplate<typename To, typename From>\ninline ::std::shared_ptr<To> down_pointer_cast(const ::std::shared_ptr<From>& f) {\n  if (false)\n  {\n    implicit_cast<From*, To*>(0);\n  }\n\n#ifndef NDEBUG\n  assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);\n#endif\n  return ::std::static_pointer_cast<To>(f);\n}\n\nnamespace net\n{\n\n// All client visible callbacks go here.\n\nclass Buffer;\nclass TcpConnection;\ntypedef std::shared_ptr<TcpConnection> TcpConnectionPtr;\ntypedef std::function<void()> TimerCallback;\ntypedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;\ntypedef std::function<void (const TcpConnectionPtr&)> CloseCallback;\ntypedef std::function<void (const TcpConnectionPtr&)> WriteCompleteCallback;\ntypedef std::function<void (const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;\n\n// the data has been read to (buf, len)\ntypedef std::function<void (const TcpConnectionPtr&,\n                            Buffer*,\n                            Timestamp)> MessageCallback;\n\nvoid defaultConnectionCallback(const TcpConnectionPtr& conn);\nvoid defaultMessageCallback(const TcpConnectionPtr& conn,\n                            Buffer* buffer,\n                            Timestamp receiveTime);\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_CALLBACKS_H\n"
  },
  {
    "path": "muduo/net/Channel.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <sstream>\n\n#include <poll.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst int Channel::kNoneEvent = 0;\nconst int Channel::kReadEvent = POLLIN | POLLPRI;\nconst int Channel::kWriteEvent = POLLOUT;\n\nChannel::Channel(EventLoop* loop, int fd__)\n  : loop_(loop),\n    fd_(fd__),\n    events_(0),\n    revents_(0),\n    index_(-1),\n    logHup_(true),\n    tied_(false),\n    eventHandling_(false),\n    addedToLoop_(false)\n{\n}\n\nChannel::~Channel()\n{\n  assert(!eventHandling_);\n  assert(!addedToLoop_);\n  if (loop_->isInLoopThread())\n  {\n    assert(!loop_->hasChannel(this));\n  }\n}\n\nvoid Channel::tie(const std::shared_ptr<void>& obj)\n{\n  tie_ = obj;\n  tied_ = true;\n}\n\nvoid Channel::update()\n{\n  addedToLoop_ = true;\n  loop_->updateChannel(this);\n}\n\nvoid Channel::remove()\n{\n  assert(isNoneEvent());\n  addedToLoop_ = false;\n  loop_->removeChannel(this);\n}\n\nvoid Channel::handleEvent(Timestamp receiveTime)\n{\n  std::shared_ptr<void> guard;\n  if (tied_)\n  {\n    guard = tie_.lock();\n    if (guard)\n    {\n      handleEventWithGuard(receiveTime);\n    }\n  }\n  else\n  {\n    handleEventWithGuard(receiveTime);\n  }\n}\n\nvoid Channel::handleEventWithGuard(Timestamp receiveTime)\n{\n  eventHandling_ = true;\n  LOG_TRACE << reventsToString();\n  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))\n  {\n    if (logHup_)\n    {\n      LOG_WARN << \"fd = \" << fd_ << \" Channel::handle_event() POLLHUP\";\n    }\n    if (closeCallback_) closeCallback_();\n  }\n\n  if (revents_ & POLLNVAL)\n  {\n    LOG_WARN << \"fd = \" << fd_ << \" Channel::handle_event() POLLNVAL\";\n  }\n\n  if (revents_ & (POLLERR | POLLNVAL))\n  {\n    if (errorCallback_) errorCallback_();\n  }\n  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))\n  {\n    if (readCallback_) readCallback_(receiveTime);\n  }\n  if (revents_ & POLLOUT)\n  {\n    if (writeCallback_) writeCallback_();\n  }\n  eventHandling_ = false;\n}\n\nstring Channel::reventsToString() const\n{\n  return eventsToString(fd_, revents_);\n}\n\nstring Channel::eventsToString() const\n{\n  return eventsToString(fd_, events_);\n}\n\nstring Channel::eventsToString(int fd, int ev)\n{\n  std::ostringstream oss;\n  oss << fd << \": \";\n  if (ev & POLLIN)\n    oss << \"IN \";\n  if (ev & POLLPRI)\n    oss << \"PRI \";\n  if (ev & POLLOUT)\n    oss << \"OUT \";\n  if (ev & POLLHUP)\n    oss << \"HUP \";\n  if (ev & POLLRDHUP)\n    oss << \"RDHUP \";\n  if (ev & POLLERR)\n    oss << \"ERR \";\n  if (ev & POLLNVAL)\n    oss << \"NVAL \";\n\n  return oss.str();\n}\n"
  },
  {
    "path": "muduo/net/Channel.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_CHANNEL_H\n#define MUDUO_NET_CHANNEL_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/Timestamp.h\"\n\n#include <functional>\n#include <memory>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass EventLoop;\n\n///\n/// A selectable I/O channel.\n///\n/// This class doesn't own the file descriptor.\n/// The file descriptor could be a socket,\n/// an eventfd, a timerfd, or a signalfd\nclass Channel : noncopyable\n{\n public:\n  typedef std::function<void()> EventCallback;\n  typedef std::function<void(Timestamp)> ReadEventCallback;\n\n  Channel(EventLoop* loop, int fd);\n  ~Channel();\n\n  void handleEvent(Timestamp receiveTime);\n  void setReadCallback(ReadEventCallback cb)\n  { readCallback_ = std::move(cb); }\n  void setWriteCallback(EventCallback cb)\n  { writeCallback_ = std::move(cb); }\n  void setCloseCallback(EventCallback cb)\n  { closeCallback_ = std::move(cb); }\n  void setErrorCallback(EventCallback cb)\n  { errorCallback_ = std::move(cb); }\n\n  /// Tie this channel to the owner object managed by shared_ptr,\n  /// prevent the owner object being destroyed in handleEvent.\n  void tie(const std::shared_ptr<void>&);\n\n  int fd() const { return fd_; }\n  int events() const { return events_; }\n  void set_revents(int revt) { revents_ = revt; } // used by pollers\n  // int revents() const { return revents_; }\n  bool isNoneEvent() const { return events_ == kNoneEvent; }\n\n  void enableReading() { events_ |= kReadEvent; update(); }\n  void disableReading() { events_ &= ~kReadEvent; update(); }\n  void enableWriting() { events_ |= kWriteEvent; update(); }\n  void disableWriting() { events_ &= ~kWriteEvent; update(); }\n  void disableAll() { events_ = kNoneEvent; update(); }\n  bool isWriting() const { return events_ & kWriteEvent; }\n  bool isReading() const { return events_ & kReadEvent; }\n\n  // for Poller\n  int index() { return index_; }\n  void set_index(int idx) { index_ = idx; }\n\n  // for debug\n  string reventsToString() const;\n  string eventsToString() const;\n\n  void doNotLogHup() { logHup_ = false; }\n\n  EventLoop* ownerLoop() { return loop_; }\n  void remove();\n\n private:\n  static string eventsToString(int fd, int ev);\n\n  void update();\n  void handleEventWithGuard(Timestamp receiveTime);\n\n  static const int kNoneEvent;\n  static const int kReadEvent;\n  static const int kWriteEvent;\n\n  EventLoop* loop_;\n  const int  fd_;\n  int        events_;\n  int        revents_; // it's the received event types of epoll or poll\n  int        index_; // used by Poller.\n  bool       logHup_;\n\n  std::weak_ptr<void> tie_;\n  bool tied_;\n  bool eventHandling_;\n  bool addedToLoop_;\n  ReadEventCallback readCallback_;\n  EventCallback writeCallback_;\n  EventCallback closeCallback_;\n  EventCallback errorCallback_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_CHANNEL_H\n"
  },
  {
    "path": "muduo/net/Connector.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/Connector.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <errno.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nconst int Connector::kMaxRetryDelayMs;\n\nConnector::Connector(EventLoop* loop, const InetAddress& serverAddr)\n  : loop_(loop),\n    serverAddr_(serverAddr),\n    connect_(false),\n    state_(kDisconnected),\n    retryDelayMs_(kInitRetryDelayMs)\n{\n  LOG_DEBUG << \"ctor[\" << this << \"]\";\n}\n\nConnector::~Connector()\n{\n  LOG_DEBUG << \"dtor[\" << this << \"]\";\n  assert(!channel_);\n}\n\nvoid Connector::start()\n{\n  connect_ = true;\n  loop_->runInLoop(std::bind(&Connector::startInLoop, this)); // FIXME: unsafe\n}\n\nvoid Connector::startInLoop()\n{\n  loop_->assertInLoopThread();\n  assert(state_ == kDisconnected);\n  if (connect_)\n  {\n    connect();\n  }\n  else\n  {\n    LOG_DEBUG << \"do not connect\";\n  }\n}\n\nvoid Connector::stop()\n{\n  connect_ = false;\n  loop_->queueInLoop(std::bind(&Connector::stopInLoop, this)); // FIXME: unsafe\n  // FIXME: cancel timer\n}\n\nvoid Connector::stopInLoop()\n{\n  loop_->assertInLoopThread();\n  if (state_ == kConnecting)\n  {\n    setState(kDisconnected);\n    int sockfd = removeAndResetChannel();\n    retry(sockfd);\n  }\n}\n\nvoid Connector::connect()\n{\n  int sockfd = sockets::createNonblockingOrDie(serverAddr_.family());\n  int ret = sockets::connect(sockfd, serverAddr_.getSockAddr());\n  int savedErrno = (ret == 0) ? 0 : errno;\n  switch (savedErrno)\n  {\n    case 0:\n    case EINPROGRESS:\n    case EINTR:\n    case EISCONN:\n      connecting(sockfd);\n      break;\n\n    case EAGAIN:\n    case EADDRINUSE:\n    case EADDRNOTAVAIL:\n    case ECONNREFUSED:\n    case ENETUNREACH:\n      retry(sockfd);\n      break;\n\n    case EACCES:\n    case EPERM:\n    case EAFNOSUPPORT:\n    case EALREADY:\n    case EBADF:\n    case EFAULT:\n    case ENOTSOCK:\n      LOG_SYSERR << \"connect error in Connector::startInLoop \" << savedErrno;\n      sockets::close(sockfd);\n      break;\n\n    default:\n      LOG_SYSERR << \"Unexpected error in Connector::startInLoop \" << savedErrno;\n      sockets::close(sockfd);\n      // connectErrorCallback_();\n      break;\n  }\n}\n\nvoid Connector::restart()\n{\n  loop_->assertInLoopThread();\n  setState(kDisconnected);\n  retryDelayMs_ = kInitRetryDelayMs;\n  connect_ = true;\n  startInLoop();\n}\n\nvoid Connector::connecting(int sockfd)\n{\n  setState(kConnecting);\n  assert(!channel_);\n  channel_.reset(new Channel(loop_, sockfd));\n  channel_->setWriteCallback(\n      std::bind(&Connector::handleWrite, this)); // FIXME: unsafe\n  channel_->setErrorCallback(\n      std::bind(&Connector::handleError, this)); // FIXME: unsafe\n\n  // channel_->tie(shared_from_this()); is not working,\n  // as channel_ is not managed by shared_ptr\n  channel_->enableWriting();\n}\n\nint Connector::removeAndResetChannel()\n{\n  channel_->disableAll();\n  channel_->remove();\n  int sockfd = channel_->fd();\n  // Can't reset channel_ here, because we are inside Channel::handleEvent\n  loop_->queueInLoop(std::bind(&Connector::resetChannel, this)); // FIXME: unsafe\n  return sockfd;\n}\n\nvoid Connector::resetChannel()\n{\n  channel_.reset();\n}\n\nvoid Connector::handleWrite()\n{\n  LOG_TRACE << \"Connector::handleWrite \" << state_;\n\n  if (state_ == kConnecting)\n  {\n    int sockfd = removeAndResetChannel();\n    int err = sockets::getSocketError(sockfd);\n    if (err)\n    {\n      LOG_WARN << \"Connector::handleWrite - SO_ERROR = \"\n               << err << \" \" << strerror_tl(err);\n      retry(sockfd);\n    }\n    else if (sockets::isSelfConnect(sockfd))\n    {\n      LOG_WARN << \"Connector::handleWrite - Self connect\";\n      retry(sockfd);\n    }\n    else\n    {\n      setState(kConnected);\n      if (connect_)\n      {\n        newConnectionCallback_(sockfd);\n      }\n      else\n      {\n        sockets::close(sockfd);\n      }\n    }\n  }\n  else\n  {\n    // what happened?\n    assert(state_ == kDisconnected);\n  }\n}\n\nvoid Connector::handleError()\n{\n  LOG_ERROR << \"Connector::handleError state=\" << state_;\n  if (state_ == kConnecting)\n  {\n    int sockfd = removeAndResetChannel();\n    int err = sockets::getSocketError(sockfd);\n    LOG_TRACE << \"SO_ERROR = \" << err << \" \" << strerror_tl(err);\n    retry(sockfd);\n  }\n}\n\nvoid Connector::retry(int sockfd)\n{\n  sockets::close(sockfd);\n  setState(kDisconnected);\n  if (connect_)\n  {\n    LOG_INFO << \"Connector::retry - Retry connecting to \" << serverAddr_.toIpPort()\n             << \" in \" << retryDelayMs_ << \" milliseconds. \";\n    loop_->runAfter(retryDelayMs_/1000.0,\n                    std::bind(&Connector::startInLoop, shared_from_this()));\n    retryDelayMs_ = std::min(retryDelayMs_ * 2, kMaxRetryDelayMs);\n  }\n  else\n  {\n    LOG_DEBUG << \"do not connect\";\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/Connector.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_CONNECTOR_H\n#define MUDUO_NET_CONNECTOR_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <functional>\n#include <memory>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Channel;\nclass EventLoop;\n\nclass Connector : noncopyable,\n                  public std::enable_shared_from_this<Connector>\n{\n public:\n  typedef std::function<void (int sockfd)> NewConnectionCallback;\n\n  Connector(EventLoop* loop, const InetAddress& serverAddr);\n  ~Connector();\n\n  void setNewConnectionCallback(const NewConnectionCallback& cb)\n  { newConnectionCallback_ = cb; }\n\n  void start();  // can be called in any thread\n  void restart();  // must be called in loop thread\n  void stop();  // can be called in any thread\n\n  const InetAddress& serverAddress() const { return serverAddr_; }\n\n private:\n  enum States { kDisconnected, kConnecting, kConnected };\n  static const int kMaxRetryDelayMs = 30*1000;\n  static const int kInitRetryDelayMs = 500;\n\n  void setState(States s) { state_ = s; }\n  void startInLoop();\n  void stopInLoop();\n  void connect();\n  void connecting(int sockfd);\n  void handleWrite();\n  void handleError();\n  void retry(int sockfd);\n  int removeAndResetChannel();\n  void resetChannel();\n\n  EventLoop* loop_;\n  InetAddress serverAddr_;\n  bool connect_; // atomic\n  States state_;  // FIXME: use atomic variable\n  std::unique_ptr<Channel> channel_;\n  NewConnectionCallback newConnectionCallback_;\n  int retryDelayMs_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_CONNECTOR_H\n"
  },
  {
    "path": "muduo/net/Endian.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_ENDIAN_H\n#define MUDUO_NET_ENDIAN_H\n\n#include <stdint.h>\n#include <endian.h>\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace sockets\n{\n\n// the inline assembler code makes type blur,\n// so we disable warnings for a while.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wconversion\"\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\ninline uint64_t hostToNetwork64(uint64_t host64)\n{\n  return htobe64(host64);\n}\n\ninline uint32_t hostToNetwork32(uint32_t host32)\n{\n  return htobe32(host32);\n}\n\ninline uint16_t hostToNetwork16(uint16_t host16)\n{\n  return htobe16(host16);\n}\n\ninline uint64_t networkToHost64(uint64_t net64)\n{\n  return be64toh(net64);\n}\n\ninline uint32_t networkToHost32(uint32_t net32)\n{\n  return be32toh(net32);\n}\n\ninline uint16_t networkToHost16(uint16_t net16)\n{\n  return be16toh(net16);\n}\n\n#pragma GCC diagnostic pop\n\n}  // namespace sockets\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_ENDIAN_H\n"
  },
  {
    "path": "muduo/net/EventLoop.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/EventLoop.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/Poller.h\"\n#include \"muduo/net/SocketsOps.h\"\n#include \"muduo/net/TimerQueue.h\"\n\n#include <algorithm>\n\n#include <signal.h>\n#include <sys/eventfd.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace\n{\n__thread EventLoop* t_loopInThisThread = 0;\n\nconst int kPollTimeMs = 10000;\n\nint createEventfd()\n{\n  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\n  if (evtfd < 0)\n  {\n    LOG_SYSERR << \"Failed in eventfd\";\n    abort();\n  }\n  return evtfd;\n}\n\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\nclass IgnoreSigPipe\n{\n public:\n  IgnoreSigPipe()\n  {\n    ::signal(SIGPIPE, SIG_IGN);\n    // LOG_TRACE << \"Ignore SIGPIPE\";\n  }\n};\n#pragma GCC diagnostic error \"-Wold-style-cast\"\n\nIgnoreSigPipe initObj;\n}  // namespace\n\nEventLoop* EventLoop::getEventLoopOfCurrentThread()\n{\n  return t_loopInThisThread;\n}\n\nEventLoop::EventLoop()\n  : looping_(false),\n    quit_(false),\n    eventHandling_(false),\n    callingPendingFunctors_(false),\n    iteration_(0),\n    threadId_(CurrentThread::tid()),\n    poller_(Poller::newDefaultPoller(this)),\n    timerQueue_(new TimerQueue(this)),\n    wakeupFd_(createEventfd()),\n    wakeupChannel_(new Channel(this, wakeupFd_)),\n    currentActiveChannel_(NULL)\n{\n  LOG_DEBUG << \"EventLoop created \" << this << \" in thread \" << threadId_;\n  if (t_loopInThisThread)\n  {\n    LOG_FATAL << \"Another EventLoop \" << t_loopInThisThread\n              << \" exists in this thread \" << threadId_;\n  }\n  else\n  {\n    t_loopInThisThread = this;\n  }\n  wakeupChannel_->setReadCallback(\n      std::bind(&EventLoop::handleRead, this));\n  // we are always reading the wakeupfd\n  wakeupChannel_->enableReading();\n}\n\nEventLoop::~EventLoop()\n{\n  LOG_DEBUG << \"EventLoop \" << this << \" of thread \" << threadId_\n            << \" destructs in thread \" << CurrentThread::tid();\n  wakeupChannel_->disableAll();\n  wakeupChannel_->remove();\n  ::close(wakeupFd_);\n  t_loopInThisThread = NULL;\n}\n\nvoid EventLoop::loop()\n{\n  assert(!looping_);\n  assertInLoopThread();\n  looping_ = true;\n  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?\n  LOG_TRACE << \"EventLoop \" << this << \" start looping\";\n\n  while (!quit_)\n  {\n    activeChannels_.clear();\n    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);\n    ++iteration_;\n    if (Logger::logLevel() <= Logger::TRACE)\n    {\n      printActiveChannels();\n    }\n    // TODO sort channel by priority\n    eventHandling_ = true;\n    for (Channel* channel : activeChannels_)\n    {\n      currentActiveChannel_ = channel;\n      currentActiveChannel_->handleEvent(pollReturnTime_);\n    }\n    currentActiveChannel_ = NULL;\n    eventHandling_ = false;\n    doPendingFunctors();\n  }\n\n  LOG_TRACE << \"EventLoop \" << this << \" stop looping\";\n  looping_ = false;\n}\n\nvoid EventLoop::quit()\n{\n  quit_ = true;\n  // There is a chance that loop() just executes while(!quit_) and exits,\n  // then EventLoop destructs, then we are accessing an invalid object.\n  // Can be fixed using mutex_ in both places.\n  if (!isInLoopThread())\n  {\n    wakeup();\n  }\n}\n\nvoid EventLoop::runInLoop(Functor cb)\n{\n  if (isInLoopThread())\n  {\n    cb();\n  }\n  else\n  {\n    queueInLoop(std::move(cb));\n  }\n}\n\nvoid EventLoop::queueInLoop(Functor cb)\n{\n  {\n  MutexLockGuard lock(mutex_);\n  pendingFunctors_.push_back(std::move(cb));\n  }\n\n  if (!isInLoopThread() || callingPendingFunctors_)\n  {\n    wakeup();\n  }\n}\n\nsize_t EventLoop::queueSize() const\n{\n  MutexLockGuard lock(mutex_);\n  return pendingFunctors_.size();\n}\n\nTimerId EventLoop::runAt(Timestamp time, TimerCallback cb)\n{\n  return timerQueue_->addTimer(std::move(cb), time, 0.0);\n}\n\nTimerId EventLoop::runAfter(double delay, TimerCallback cb)\n{\n  Timestamp time(addTime(Timestamp::now(), delay));\n  return runAt(time, std::move(cb));\n}\n\nTimerId EventLoop::runEvery(double interval, TimerCallback cb)\n{\n  Timestamp time(addTime(Timestamp::now(), interval));\n  return timerQueue_->addTimer(std::move(cb), time, interval);\n}\n\nvoid EventLoop::cancel(TimerId timerId)\n{\n  return timerQueue_->cancel(timerId);\n}\n\nvoid EventLoop::updateChannel(Channel* channel)\n{\n  assert(channel->ownerLoop() == this);\n  assertInLoopThread();\n  poller_->updateChannel(channel);\n}\n\nvoid EventLoop::removeChannel(Channel* channel)\n{\n  assert(channel->ownerLoop() == this);\n  assertInLoopThread();\n  if (eventHandling_)\n  {\n    assert(currentActiveChannel_ == channel ||\n        std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());\n  }\n  poller_->removeChannel(channel);\n}\n\nbool EventLoop::hasChannel(Channel* channel)\n{\n  assert(channel->ownerLoop() == this);\n  assertInLoopThread();\n  return poller_->hasChannel(channel);\n}\n\nvoid EventLoop::abortNotInLoopThread()\n{\n  LOG_FATAL << \"EventLoop::abortNotInLoopThread - EventLoop \" << this\n            << \" was created in threadId_ = \" << threadId_\n            << \", current thread id = \" <<  CurrentThread::tid();\n}\n\nvoid EventLoop::wakeup()\n{\n  uint64_t one = 1;\n  ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);\n  if (n != sizeof one)\n  {\n    LOG_ERROR << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 8\";\n  }\n}\n\nvoid EventLoop::handleRead()\n{\n  uint64_t one = 1;\n  ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);\n  if (n != sizeof one)\n  {\n    LOG_ERROR << \"EventLoop::handleRead() reads \" << n << \" bytes instead of 8\";\n  }\n}\n\nvoid EventLoop::doPendingFunctors()\n{\n  std::vector<Functor> functors;\n  callingPendingFunctors_ = true;\n\n  {\n  MutexLockGuard lock(mutex_);\n  functors.swap(pendingFunctors_);\n  }\n\n  for (const Functor& functor : functors)\n  {\n    functor();\n  }\n  callingPendingFunctors_ = false;\n}\n\nvoid EventLoop::printActiveChannels() const\n{\n  for (const Channel* channel : activeChannels_)\n  {\n    LOG_TRACE << \"{\" << channel->reventsToString() << \"} \";\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/EventLoop.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_EVENTLOOP_H\n#define MUDUO_NET_EVENTLOOP_H\n\n#include <atomic>\n#include <functional>\n#include <vector>\n\n#include <boost/any.hpp>\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/CurrentThread.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/Callbacks.h\"\n#include \"muduo/net/TimerId.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Channel;\nclass Poller;\nclass TimerQueue;\n\n///\n/// Reactor, at most one per thread.\n///\n/// This is an interface class, so don't expose too much details.\nclass EventLoop : noncopyable\n{\n public:\n  typedef std::function<void()> Functor;\n\n  EventLoop();\n  ~EventLoop();  // force out-line dtor, for std::unique_ptr members.\n\n  ///\n  /// Loops forever.\n  ///\n  /// Must be called in the same thread as creation of the object.\n  ///\n  void loop();\n\n  /// Quits loop.\n  ///\n  /// This is not 100% thread safe, if you call through a raw pointer,\n  /// better to call through shared_ptr<EventLoop> for 100% safety.\n  void quit();\n\n  ///\n  /// Time when poll returns, usually means data arrival.\n  ///\n  Timestamp pollReturnTime() const { return pollReturnTime_; }\n\n  int64_t iteration() const { return iteration_; }\n\n  /// Runs callback immediately in the loop thread.\n  /// It wakes up the loop, and run the cb.\n  /// If in the same loop thread, cb is run within the function.\n  /// Safe to call from other threads.\n  void runInLoop(Functor cb);\n  /// Queues callback in the loop thread.\n  /// Runs after finish pooling.\n  /// Safe to call from other threads.\n  void queueInLoop(Functor cb);\n\n  size_t queueSize() const;\n\n  // timers\n\n  ///\n  /// Runs callback at 'time'.\n  /// Safe to call from other threads.\n  ///\n  TimerId runAt(Timestamp time, TimerCallback cb);\n  ///\n  /// Runs callback after @c delay seconds.\n  /// Safe to call from other threads.\n  ///\n  TimerId runAfter(double delay, TimerCallback cb);\n  ///\n  /// Runs callback every @c interval seconds.\n  /// Safe to call from other threads.\n  ///\n  TimerId runEvery(double interval, TimerCallback cb);\n  ///\n  /// Cancels the timer.\n  /// Safe to call from other threads.\n  ///\n  void cancel(TimerId timerId);\n\n  // internal usage\n  void wakeup();\n  void updateChannel(Channel* channel);\n  void removeChannel(Channel* channel);\n  bool hasChannel(Channel* channel);\n\n  // pid_t threadId() const { return threadId_; }\n  void assertInLoopThread()\n  {\n    if (!isInLoopThread())\n    {\n      abortNotInLoopThread();\n    }\n  }\n  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }\n  // bool callingPendingFunctors() const { return callingPendingFunctors_; }\n  bool eventHandling() const { return eventHandling_; }\n\n  void setContext(const boost::any& context)\n  { context_ = context; }\n\n  const boost::any& getContext() const\n  { return context_; }\n\n  boost::any* getMutableContext()\n  { return &context_; }\n\n  static EventLoop* getEventLoopOfCurrentThread();\n\n private:\n  void abortNotInLoopThread();\n  void handleRead();  // waked up\n  void doPendingFunctors();\n\n  void printActiveChannels() const; // DEBUG\n\n  typedef std::vector<Channel*> ChannelList;\n\n  bool looping_; /* atomic */\n  std::atomic<bool> quit_;\n  bool eventHandling_; /* atomic */\n  bool callingPendingFunctors_; /* atomic */\n  int64_t iteration_;\n  const pid_t threadId_;\n  Timestamp pollReturnTime_;\n  std::unique_ptr<Poller> poller_;\n  std::unique_ptr<TimerQueue> timerQueue_;\n  int wakeupFd_;\n  // unlike in TimerQueue, which is an internal class,\n  // we don't expose Channel to client.\n  std::unique_ptr<Channel> wakeupChannel_;\n  boost::any context_;\n\n  // scratch variables\n  ChannelList activeChannels_;\n  Channel* currentActiveChannel_;\n\n  mutable MutexLock mutex_;\n  std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_EVENTLOOP_H\n"
  },
  {
    "path": "muduo/net/EventLoopThread.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/EventLoopThread.h\"\n\n#include \"muduo/net/EventLoop.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoopThread::EventLoopThread(const ThreadInitCallback& cb,\n                                 const string& name)\n  : loop_(NULL),\n    exiting_(false),\n    thread_(std::bind(&EventLoopThread::threadFunc, this), name),\n    mutex_(),\n    cond_(mutex_),\n    callback_(cb)\n{\n}\n\nEventLoopThread::~EventLoopThread()\n{\n  exiting_ = true;\n  if (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_.\n  {\n    // still a tiny chance to call destructed object, if threadFunc exits just now.\n    // but when EventLoopThread destructs, usually programming is exiting anyway.\n    loop_->quit();\n    thread_.join();\n  }\n}\n\nEventLoop* EventLoopThread::startLoop()\n{\n  assert(!thread_.started());\n  thread_.start();\n\n  EventLoop* loop = NULL;\n  {\n    MutexLockGuard lock(mutex_);\n    while (loop_ == NULL)\n    {\n      cond_.wait();\n    }\n    loop = loop_;\n  }\n\n  return loop;\n}\n\nvoid EventLoopThread::threadFunc()\n{\n  EventLoop loop;\n\n  if (callback_)\n  {\n    callback_(&loop);\n  }\n\n  {\n    MutexLockGuard lock(mutex_);\n    loop_ = &loop;\n    cond_.notify();\n  }\n\n  loop.loop();\n  //assert(exiting_);\n  MutexLockGuard lock(mutex_);\n  loop_ = NULL;\n}\n\n"
  },
  {
    "path": "muduo/net/EventLoopThread.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_EVENTLOOPTHREAD_H\n#define MUDUO_NET_EVENTLOOPTHREAD_H\n\n#include \"muduo/base/Condition.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Thread.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass EventLoop;\n\nclass EventLoopThread : noncopyable\n{\n public:\n  typedef std::function<void(EventLoop*)> ThreadInitCallback;\n\n  EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(),\n                  const string& name = string());\n  ~EventLoopThread();\n  EventLoop* startLoop();\n\n private:\n  void threadFunc();\n\n  EventLoop* loop_ GUARDED_BY(mutex_);\n  bool exiting_;\n  Thread thread_;\n  MutexLock mutex_;\n  Condition cond_ GUARDED_BY(mutex_);\n  ThreadInitCallback callback_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_EVENTLOOPTHREAD_H\n\n"
  },
  {
    "path": "muduo/net/EventLoopThreadPool.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/EventLoopThreadPool.h\"\n\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg)\n  : baseLoop_(baseLoop),\n    name_(nameArg),\n    started_(false),\n    numThreads_(0),\n    next_(0)\n{\n}\n\nEventLoopThreadPool::~EventLoopThreadPool()\n{\n  // Don't delete loop, it's stack variable\n}\n\nvoid EventLoopThreadPool::start(const ThreadInitCallback& cb)\n{\n  assert(!started_);\n  baseLoop_->assertInLoopThread();\n\n  started_ = true;\n\n  for (int i = 0; i < numThreads_; ++i)\n  {\n    char buf[name_.size() + 32];\n    snprintf(buf, sizeof buf, \"%s%d\", name_.c_str(), i);\n    EventLoopThread* t = new EventLoopThread(cb, buf);\n    threads_.push_back(std::unique_ptr<EventLoopThread>(t));\n    loops_.push_back(t->startLoop());\n  }\n  if (numThreads_ == 0 && cb)\n  {\n    cb(baseLoop_);\n  }\n}\n\nEventLoop* EventLoopThreadPool::getNextLoop()\n{\n  baseLoop_->assertInLoopThread();\n  assert(started_);\n  EventLoop* loop = baseLoop_;\n\n  if (!loops_.empty())\n  {\n    // round-robin\n    loop = loops_[next_];\n    ++next_;\n    if (implicit_cast<size_t>(next_) >= loops_.size())\n    {\n      next_ = 0;\n    }\n  }\n  return loop;\n}\n\nEventLoop* EventLoopThreadPool::getLoopForHash(size_t hashCode)\n{\n  baseLoop_->assertInLoopThread();\n  EventLoop* loop = baseLoop_;\n\n  if (!loops_.empty())\n  {\n    loop = loops_[hashCode % loops_.size()];\n  }\n  return loop;\n}\n\nstd::vector<EventLoop*> EventLoopThreadPool::getAllLoops()\n{\n  baseLoop_->assertInLoopThread();\n  assert(started_);\n  if (loops_.empty())\n  {\n    return std::vector<EventLoop*>(1, baseLoop_);\n  }\n  else\n  {\n    return loops_;\n  }\n}\n"
  },
  {
    "path": "muduo/net/EventLoopThreadPool.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_EVENTLOOPTHREADPOOL_H\n#define MUDUO_NET_EVENTLOOPTHREADPOOL_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/Types.h\"\n\n#include <functional>\n#include <memory>\n#include <vector>\n\nnamespace muduo\n{\n\nnamespace net\n{\n\nclass EventLoop;\nclass EventLoopThread;\n\nclass EventLoopThreadPool : noncopyable\n{\n public:\n  typedef std::function<void(EventLoop*)> ThreadInitCallback;\n\n  EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);\n  ~EventLoopThreadPool();\n  void setThreadNum(int numThreads) { numThreads_ = numThreads; }\n  void start(const ThreadInitCallback& cb = ThreadInitCallback());\n\n  // valid after calling start()\n  /// round-robin\n  EventLoop* getNextLoop();\n\n  /// with the same hash code, it will always return the same EventLoop\n  EventLoop* getLoopForHash(size_t hashCode);\n\n  std::vector<EventLoop*> getAllLoops();\n\n  bool started() const\n  { return started_; }\n\n  const string& name() const\n  { return name_; }\n\n private:\n\n  EventLoop* baseLoop_;\n  string name_;\n  bool started_;\n  int numThreads_;\n  int next_;\n  std::vector<std::unique_ptr<EventLoopThread>> threads_;\n  std::vector<EventLoop*> loops_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_EVENTLOOPTHREADPOOL_H\n"
  },
  {
    "path": "muduo/net/InetAddress.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/InetAddress.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <netdb.h>\n#include <netinet/in.h>\n\n// INADDR_ANY use (type)value casting.\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\nstatic const in_addr_t kInaddrAny = INADDR_ANY;\nstatic const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;\n#pragma GCC diagnostic error \"-Wold-style-cast\"\n\n//     /* Structure describing an Internet socket address.  */\n//     struct sockaddr_in {\n//         sa_family_t    sin_family; /* address family: AF_INET */\n//         uint16_t       sin_port;   /* port in network byte order */\n//         struct in_addr sin_addr;   /* internet address */\n//     };\n\n//     /* Internet address. */\n//     typedef uint32_t in_addr_t;\n//     struct in_addr {\n//         in_addr_t       s_addr;     /* address in network byte order */\n//     };\n\n//     struct sockaddr_in6 {\n//         sa_family_t     sin6_family;   /* address family: AF_INET6 */\n//         uint16_t        sin6_port;     /* port in network byte order */\n//         uint32_t        sin6_flowinfo; /* IPv6 flow information */\n//         struct in6_addr sin6_addr;     /* IPv6 address */\n//         uint32_t        sin6_scope_id; /* IPv6 scope-id */\n//     };\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nstatic_assert(sizeof(InetAddress) == sizeof(struct sockaddr_in6),\n              \"InetAddress is same size as sockaddr_in6\");\nstatic_assert(offsetof(sockaddr_in, sin_family) == 0, \"sin_family offset 0\");\nstatic_assert(offsetof(sockaddr_in6, sin6_family) == 0, \"sin6_family offset 0\");\nstatic_assert(offsetof(sockaddr_in, sin_port) == 2, \"sin_port offset 2\");\nstatic_assert(offsetof(sockaddr_in6, sin6_port) == 2, \"sin6_port offset 2\");\n\nInetAddress::InetAddress(uint16_t portArg, bool loopbackOnly, bool ipv6)\n{\n  static_assert(offsetof(InetAddress, addr6_) == 0, \"addr6_ offset 0\");\n  static_assert(offsetof(InetAddress, addr_) == 0, \"addr_ offset 0\");\n  if (ipv6)\n  {\n    memZero(&addr6_, sizeof addr6_);\n    addr6_.sin6_family = AF_INET6;\n    in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;\n    addr6_.sin6_addr = ip;\n    addr6_.sin6_port = sockets::hostToNetwork16(portArg);\n  }\n  else\n  {\n    memZero(&addr_, sizeof addr_);\n    addr_.sin_family = AF_INET;\n    in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;\n    addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);\n    addr_.sin_port = sockets::hostToNetwork16(portArg);\n  }\n}\n\nInetAddress::InetAddress(StringArg ip, uint16_t portArg, bool ipv6)\n{\n  if (ipv6 || strchr(ip.c_str(), ':'))\n  {\n    memZero(&addr6_, sizeof addr6_);\n    sockets::fromIpPort(ip.c_str(), portArg, &addr6_);\n  }\n  else\n  {\n    memZero(&addr_, sizeof addr_);\n    sockets::fromIpPort(ip.c_str(), portArg, &addr_);\n  }\n}\n\nstring InetAddress::toIpPort() const\n{\n  char buf[64] = \"\";\n  sockets::toIpPort(buf, sizeof buf, getSockAddr());\n  return buf;\n}\n\nstring InetAddress::toIp() const\n{\n  char buf[64] = \"\";\n  sockets::toIp(buf, sizeof buf, getSockAddr());\n  return buf;\n}\n\nuint32_t InetAddress::ipv4NetEndian() const\n{\n  assert(family() == AF_INET);\n  return addr_.sin_addr.s_addr;\n}\n\nuint16_t InetAddress::port() const\n{\n  return sockets::networkToHost16(portNetEndian());\n}\n\nstatic __thread char t_resolveBuffer[64 * 1024];\n\nbool InetAddress::resolve(StringArg hostname, InetAddress* out)\n{\n  assert(out != NULL);\n  struct hostent hent;\n  struct hostent* he = NULL;\n  int herrno = 0;\n  memZero(&hent, sizeof(hent));\n\n  int ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno);\n  if (ret == 0 && he != NULL)\n  {\n    assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));\n    out->addr_.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);\n    return true;\n  }\n  else\n  {\n    if (ret)\n    {\n      LOG_SYSERR << \"InetAddress::resolve\";\n    }\n    return false;\n  }\n}\n\nvoid InetAddress::setScopeId(uint32_t scope_id)\n{\n  if (family() == AF_INET6)\n  {\n    addr6_.sin6_scope_id = scope_id;\n  }\n}\n"
  },
  {
    "path": "muduo/net/InetAddress.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_INETADDRESS_H\n#define MUDUO_NET_INETADDRESS_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/StringPiece.h\"\n\n#include <netinet/in.h>\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace sockets\n{\nconst struct sockaddr* sockaddr_cast(const struct sockaddr_in6* addr);\n}\n\n///\n/// Wrapper of sockaddr_in.\n///\n/// This is an POD interface class.\nclass InetAddress : public muduo::copyable\n{\n public:\n  /// Constructs an endpoint with given port number.\n  /// Mostly used in TcpServer listening.\n  explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false);\n\n  /// Constructs an endpoint with given ip and port.\n  /// @c ip should be \"1.2.3.4\"\n  InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);\n\n  /// Constructs an endpoint with given struct @c sockaddr_in\n  /// Mostly used when accepting new connections\n  explicit InetAddress(const struct sockaddr_in& addr)\n    : addr_(addr)\n  { }\n\n  explicit InetAddress(const struct sockaddr_in6& addr)\n    : addr6_(addr)\n  { }\n\n  sa_family_t family() const { return addr_.sin_family; }\n  string toIp() const;\n  string toIpPort() const;\n  uint16_t port() const;\n\n  // default copy/assignment are Okay\n\n  const struct sockaddr* getSockAddr() const { return sockets::sockaddr_cast(&addr6_); }\n  void setSockAddrInet6(const struct sockaddr_in6& addr6) { addr6_ = addr6; }\n\n  uint32_t ipv4NetEndian() const;\n  uint16_t portNetEndian() const { return addr_.sin_port; }\n\n  // resolve hostname to IP address, not changing port or sin_family\n  // return true on success.\n  // thread safe\n  static bool resolve(StringArg hostname, InetAddress* result);\n  // static std::vector<InetAddress> resolveAll(const char* hostname, uint16_t port = 0);\n\n  // set IPv6 ScopeID\n  void setScopeId(uint32_t scope_id);\n\n private:\n  union\n  {\n    struct sockaddr_in addr_;\n    struct sockaddr_in6 addr6_;\n  };\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_INETADDRESS_H\n"
  },
  {
    "path": "muduo/net/Poller.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/Poller.h\"\n\n#include \"muduo/net/Channel.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nPoller::Poller(EventLoop* loop)\n  : ownerLoop_(loop)\n{\n}\n\nPoller::~Poller() = default;\n\nbool Poller::hasChannel(Channel* channel) const\n{\n  assertInLoopThread();\n  ChannelMap::const_iterator it = channels_.find(channel->fd());\n  return it != channels_.end() && it->second == channel;\n}\n\n"
  },
  {
    "path": "muduo/net/Poller.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_POLLER_H\n#define MUDUO_NET_POLLER_H\n\n#include <map>\n#include <vector>\n\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/EventLoop.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Channel;\n\n///\n/// Base class for IO Multiplexing\n///\n/// This class doesn't own the Channel objects.\nclass Poller : noncopyable\n{\n public:\n  typedef std::vector<Channel*> ChannelList;\n\n  Poller(EventLoop* loop);\n  virtual ~Poller();\n\n  /// Polls the I/O events.\n  /// Must be called in the loop thread.\n  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;\n\n  /// Changes the interested I/O events.\n  /// Must be called in the loop thread.\n  virtual void updateChannel(Channel* channel) = 0;\n\n  /// Remove the channel, when it destructs.\n  /// Must be called in the loop thread.\n  virtual void removeChannel(Channel* channel) = 0;\n\n  virtual bool hasChannel(Channel* channel) const;\n\n  static Poller* newDefaultPoller(EventLoop* loop);\n\n  void assertInLoopThread() const\n  {\n    ownerLoop_->assertInLoopThread();\n  }\n\n protected:\n  typedef std::map<int, Channel*> ChannelMap;\n  ChannelMap channels_;\n\n private:\n  EventLoop* ownerLoop_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_POLLER_H\n"
  },
  {
    "path": "muduo/net/Socket.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/Socket.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/InetAddress.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <stdio.h>  // snprintf\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nSocket::~Socket()\n{\n  sockets::close(sockfd_);\n}\n\nbool Socket::getTcpInfo(struct tcp_info* tcpi) const\n{\n  socklen_t len = sizeof(*tcpi);\n  memZero(tcpi, len);\n  return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;\n}\n\nbool Socket::getTcpInfoString(char* buf, int len) const\n{\n  struct tcp_info tcpi;\n  bool ok = getTcpInfo(&tcpi);\n  if (ok)\n  {\n    snprintf(buf, len, \"unrecovered=%u \"\n             \"rto=%u ato=%u snd_mss=%u rcv_mss=%u \"\n             \"lost=%u retrans=%u rtt=%u rttvar=%u \"\n             \"sshthresh=%u cwnd=%u total_retrans=%u\",\n             tcpi.tcpi_retransmits,  // Number of unrecovered [RTO] timeouts\n             tcpi.tcpi_rto,          // Retransmit timeout in usec\n             tcpi.tcpi_ato,          // Predicted tick of soft clock in usec\n             tcpi.tcpi_snd_mss,\n             tcpi.tcpi_rcv_mss,\n             tcpi.tcpi_lost,         // Lost packets\n             tcpi.tcpi_retrans,      // Retransmitted packets out\n             tcpi.tcpi_rtt,          // Smoothed round trip time in usec\n             tcpi.tcpi_rttvar,       // Medium deviation\n             tcpi.tcpi_snd_ssthresh,\n             tcpi.tcpi_snd_cwnd,\n             tcpi.tcpi_total_retrans);  // Total retransmits for entire connection\n  }\n  return ok;\n}\n\nvoid Socket::bindAddress(const InetAddress& addr)\n{\n  sockets::bindOrDie(sockfd_, addr.getSockAddr());\n}\n\nvoid Socket::listen()\n{\n  sockets::listenOrDie(sockfd_);\n}\n\nint Socket::accept(InetAddress* peeraddr)\n{\n  struct sockaddr_in6 addr;\n  memZero(&addr, sizeof addr);\n  int connfd = sockets::accept(sockfd_, &addr);\n  if (connfd >= 0)\n  {\n    peeraddr->setSockAddrInet6(addr);\n  }\n  return connfd;\n}\n\nvoid Socket::shutdownWrite()\n{\n  sockets::shutdownWrite(sockfd_);\n}\n\nvoid Socket::setTcpNoDelay(bool on)\n{\n  int optval = on ? 1 : 0;\n  ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,\n               &optval, static_cast<socklen_t>(sizeof optval));\n  // FIXME CHECK\n}\n\nvoid Socket::setReuseAddr(bool on)\n{\n  int optval = on ? 1 : 0;\n  ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,\n               &optval, static_cast<socklen_t>(sizeof optval));\n  // FIXME CHECK\n}\n\nvoid Socket::setReusePort(bool on)\n{\n#ifdef SO_REUSEPORT\n  int optval = on ? 1 : 0;\n  int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT,\n                         &optval, static_cast<socklen_t>(sizeof optval));\n  if (ret < 0 && on)\n  {\n    LOG_SYSERR << \"SO_REUSEPORT failed.\";\n  }\n#else\n  if (on)\n  {\n    LOG_ERROR << \"SO_REUSEPORT is not supported.\";\n  }\n#endif\n}\n\nvoid Socket::setKeepAlive(bool on)\n{\n  int optval = on ? 1 : 0;\n  ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,\n               &optval, static_cast<socklen_t>(sizeof optval));\n  // FIXME CHECK\n}\n\n"
  },
  {
    "path": "muduo/net/Socket.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_SOCKET_H\n#define MUDUO_NET_SOCKET_H\n\n#include \"muduo/base/noncopyable.h\"\n\n// struct tcp_info is in <netinet/tcp.h>\nstruct tcp_info;\n\nnamespace muduo\n{\n///\n/// TCP networking.\n///\nnamespace net\n{\n\nclass InetAddress;\n\n///\n/// Wrapper of socket file descriptor.\n///\n/// It closes the sockfd when desctructs.\n/// It's thread safe, all operations are delagated to OS.\nclass Socket : noncopyable\n{\n public:\n  explicit Socket(int sockfd)\n    : sockfd_(sockfd)\n  { }\n\n  // Socket(Socket&&) // move constructor in C++11\n  ~Socket();\n\n  int fd() const { return sockfd_; }\n  // return true if success.\n  bool getTcpInfo(struct tcp_info*) const;\n  bool getTcpInfoString(char* buf, int len) const;\n\n  /// abort if address in use\n  void bindAddress(const InetAddress& localaddr);\n  /// abort if address in use\n  void listen();\n\n  /// On success, returns a non-negative integer that is\n  /// a descriptor for the accepted socket, which has been\n  /// set to non-blocking and close-on-exec. *peeraddr is assigned.\n  /// On error, -1 is returned, and *peeraddr is untouched.\n  int accept(InetAddress* peeraddr);\n\n  void shutdownWrite();\n\n  ///\n  /// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).\n  ///\n  void setTcpNoDelay(bool on);\n\n  ///\n  /// Enable/disable SO_REUSEADDR\n  ///\n  void setReuseAddr(bool on);\n\n  ///\n  /// Enable/disable SO_REUSEPORT\n  ///\n  void setReusePort(bool on);\n\n  ///\n  /// Enable/disable SO_KEEPALIVE\n  ///\n  void setKeepAlive(bool on);\n\n private:\n  const int sockfd_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_SOCKET_H\n"
  },
  {
    "path": "muduo/net/SocketsOps.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/SocketsOps.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/Endian.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>  // snprintf\n#include <sys/socket.h>\n#include <sys/uio.h>  // readv\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace\n{\n\ntypedef struct sockaddr SA;\n\n\n#if VALGRIND || defined (NO_ACCEPT4)\nvoid setNonBlockAndCloseOnExec(int sockfd)\n{\n  // non-block\n  int flags = ::fcntl(sockfd, F_GETFL, 0);\n  flags |= O_NONBLOCK;\n  int ret = ::fcntl(sockfd, F_SETFL, flags);\n  // FIXME check\n\n  // close-on-exec\n  flags = ::fcntl(sockfd, F_GETFD, 0);\n  flags |= FD_CLOEXEC;\n  ret = ::fcntl(sockfd, F_SETFD, flags);\n  // FIXME check\n\n  (void)ret;\n}\n#endif\n\n}  // namespace\n\nconst struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in6* addr)\n{\n  return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));\n}\n\nstruct sockaddr* sockets::sockaddr_cast(struct sockaddr_in6* addr)\n{\n  return static_cast<struct sockaddr*>(implicit_cast<void*>(addr));\n}\n\nconst struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)\n{\n  return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));\n}\n\nconst struct sockaddr_in* sockets::sockaddr_in_cast(const struct sockaddr* addr)\n{\n  return static_cast<const struct sockaddr_in*>(implicit_cast<const void*>(addr));\n}\n\nconst struct sockaddr_in6* sockets::sockaddr_in6_cast(const struct sockaddr* addr)\n{\n  return static_cast<const struct sockaddr_in6*>(implicit_cast<const void*>(addr));\n}\n\nint sockets::createNonblockingOrDie(sa_family_t family)\n{\n#if VALGRIND\n  int sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP);\n  if (sockfd < 0)\n  {\n    LOG_SYSFATAL << \"sockets::createNonblockingOrDie\";\n  }\n\n  setNonBlockAndCloseOnExec(sockfd);\n#else\n  int sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);\n  if (sockfd < 0)\n  {\n    LOG_SYSFATAL << \"sockets::createNonblockingOrDie\";\n  }\n#endif\n  return sockfd;\n}\n\nvoid sockets::bindOrDie(int sockfd, const struct sockaddr* addr)\n{\n  int ret = ::bind(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));\n  if (ret < 0)\n  {\n    LOG_SYSFATAL << \"sockets::bindOrDie\";\n  }\n}\n\nvoid sockets::listenOrDie(int sockfd)\n{\n  int ret = ::listen(sockfd, SOMAXCONN);\n  if (ret < 0)\n  {\n    LOG_SYSFATAL << \"sockets::listenOrDie\";\n  }\n}\n\nint sockets::accept(int sockfd, struct sockaddr_in6* addr)\n{\n  socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);\n#if VALGRIND || defined (NO_ACCEPT4)\n  int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);\n  setNonBlockAndCloseOnExec(connfd);\n#else\n  int connfd = ::accept4(sockfd, sockaddr_cast(addr),\n                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);\n#endif\n  if (connfd < 0)\n  {\n    int savedErrno = errno;\n    LOG_SYSERR << \"Socket::accept\";\n    switch (savedErrno)\n    {\n      case EAGAIN:\n      case ECONNABORTED:\n      case EINTR:\n      case EPROTO: // ???\n      case EPERM:\n      case EMFILE: // per-process lmit of open file desctiptor ???\n        // expected errors\n        errno = savedErrno;\n        break;\n      case EBADF:\n      case EFAULT:\n      case EINVAL:\n      case ENFILE:\n      case ENOBUFS:\n      case ENOMEM:\n      case ENOTSOCK:\n      case EOPNOTSUPP:\n        // unexpected errors\n        LOG_FATAL << \"unexpected error of ::accept \" << savedErrno;\n        break;\n      default:\n        LOG_FATAL << \"unknown error of ::accept \" << savedErrno;\n        break;\n    }\n  }\n  return connfd;\n}\n\nint sockets::connect(int sockfd, const struct sockaddr* addr)\n{\n  return ::connect(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));\n}\n\nssize_t sockets::read(int sockfd, void *buf, size_t count)\n{\n  return ::read(sockfd, buf, count);\n}\n\nssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)\n{\n  return ::readv(sockfd, iov, iovcnt);\n}\n\nssize_t sockets::write(int sockfd, const void *buf, size_t count)\n{\n  return ::write(sockfd, buf, count);\n}\n\nvoid sockets::close(int sockfd)\n{\n  if (::close(sockfd) < 0)\n  {\n    LOG_SYSERR << \"sockets::close\";\n  }\n}\n\nvoid sockets::shutdownWrite(int sockfd)\n{\n  if (::shutdown(sockfd, SHUT_WR) < 0)\n  {\n    LOG_SYSERR << \"sockets::shutdownWrite\";\n  }\n}\n\nvoid sockets::toIpPort(char* buf, size_t size,\n                       const struct sockaddr* addr)\n{\n  if (addr->sa_family == AF_INET6)\n  {\n    buf[0] = '[';\n    toIp(buf+1, size-1, addr);\n    size_t end = ::strlen(buf);\n    const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);\n    uint16_t port = sockets::networkToHost16(addr6->sin6_port);\n    assert(size > end);\n    snprintf(buf+end, size-end, \"]:%u\", port);\n    return;\n  }\n  toIp(buf, size, addr);\n  size_t end = ::strlen(buf);\n  const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);\n  uint16_t port = sockets::networkToHost16(addr4->sin_port);\n  assert(size > end);\n  snprintf(buf+end, size-end, \":%u\", port);\n}\n\nvoid sockets::toIp(char* buf, size_t size,\n                   const struct sockaddr* addr)\n{\n  if (addr->sa_family == AF_INET)\n  {\n    assert(size >= INET_ADDRSTRLEN);\n    const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);\n    ::inet_ntop(AF_INET, &addr4->sin_addr, buf, static_cast<socklen_t>(size));\n  }\n  else if (addr->sa_family == AF_INET6)\n  {\n    assert(size >= INET6_ADDRSTRLEN);\n    const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);\n    ::inet_ntop(AF_INET6, &addr6->sin6_addr, buf, static_cast<socklen_t>(size));\n  }\n}\n\nvoid sockets::fromIpPort(const char* ip, uint16_t port,\n                         struct sockaddr_in* addr)\n{\n  addr->sin_family = AF_INET;\n  addr->sin_port = hostToNetwork16(port);\n  if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)\n  {\n    LOG_SYSERR << \"sockets::fromIpPort\";\n  }\n}\n\nvoid sockets::fromIpPort(const char* ip, uint16_t port,\n                         struct sockaddr_in6* addr)\n{\n  addr->sin6_family = AF_INET6;\n  addr->sin6_port = hostToNetwork16(port);\n  if (::inet_pton(AF_INET6, ip, &addr->sin6_addr) <= 0)\n  {\n    LOG_SYSERR << \"sockets::fromIpPort\";\n  }\n}\n\nint sockets::getSocketError(int sockfd)\n{\n  int optval;\n  socklen_t optlen = static_cast<socklen_t>(sizeof optval);\n\n  if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)\n  {\n    return errno;\n  }\n  else\n  {\n    return optval;\n  }\n}\n\nstruct sockaddr_in6 sockets::getLocalAddr(int sockfd)\n{\n  struct sockaddr_in6 localaddr;\n  memZero(&localaddr, sizeof localaddr);\n  socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);\n  if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)\n  {\n    LOG_SYSERR << \"sockets::getLocalAddr\";\n  }\n  return localaddr;\n}\n\nstruct sockaddr_in6 sockets::getPeerAddr(int sockfd)\n{\n  struct sockaddr_in6 peeraddr;\n  memZero(&peeraddr, sizeof peeraddr);\n  socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);\n  if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)\n  {\n    LOG_SYSERR << \"sockets::getPeerAddr\";\n  }\n  return peeraddr;\n}\n\nbool sockets::isSelfConnect(int sockfd)\n{\n  struct sockaddr_in6 localaddr = getLocalAddr(sockfd);\n  struct sockaddr_in6 peeraddr = getPeerAddr(sockfd);\n  if (localaddr.sin6_family == AF_INET)\n  {\n    const struct sockaddr_in* laddr4 = reinterpret_cast<struct sockaddr_in*>(&localaddr);\n    const struct sockaddr_in* raddr4 = reinterpret_cast<struct sockaddr_in*>(&peeraddr);\n    return laddr4->sin_port == raddr4->sin_port\n        && laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr;\n  }\n  else if (localaddr.sin6_family == AF_INET6)\n  {\n    return localaddr.sin6_port == peeraddr.sin6_port\n        && memcmp(&localaddr.sin6_addr, &peeraddr.sin6_addr, sizeof localaddr.sin6_addr) == 0;\n  }\n  else\n  {\n    return false;\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/SocketsOps.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_SOCKETSOPS_H\n#define MUDUO_NET_SOCKETSOPS_H\n\n#include <arpa/inet.h>\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace sockets\n{\n\n///\n/// Creates a non-blocking socket file descriptor,\n/// abort if any error.\nint createNonblockingOrDie(sa_family_t family);\n\nint  connect(int sockfd, const struct sockaddr* addr);\nvoid bindOrDie(int sockfd, const struct sockaddr* addr);\nvoid listenOrDie(int sockfd);\nint  accept(int sockfd, struct sockaddr_in6* addr);\nssize_t read(int sockfd, void *buf, size_t count);\nssize_t readv(int sockfd, const struct iovec *iov, int iovcnt);\nssize_t write(int sockfd, const void *buf, size_t count);\nvoid close(int sockfd);\nvoid shutdownWrite(int sockfd);\n\nvoid toIpPort(char* buf, size_t size,\n              const struct sockaddr* addr);\nvoid toIp(char* buf, size_t size,\n          const struct sockaddr* addr);\n\nvoid fromIpPort(const char* ip, uint16_t port,\n                struct sockaddr_in* addr);\nvoid fromIpPort(const char* ip, uint16_t port,\n                struct sockaddr_in6* addr);\n\nint getSocketError(int sockfd);\n\nconst struct sockaddr* sockaddr_cast(const struct sockaddr_in* addr);\nconst struct sockaddr* sockaddr_cast(const struct sockaddr_in6* addr);\nstruct sockaddr* sockaddr_cast(struct sockaddr_in6* addr);\nconst struct sockaddr_in* sockaddr_in_cast(const struct sockaddr* addr);\nconst struct sockaddr_in6* sockaddr_in6_cast(const struct sockaddr* addr);\n\nstruct sockaddr_in6 getLocalAddr(int sockfd);\nstruct sockaddr_in6 getPeerAddr(int sockfd);\nbool isSelfConnect(int sockfd);\n\n}  // namespace sockets\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_SOCKETSOPS_H\n"
  },
  {
    "path": "muduo/net/TcpClient.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Connector.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <stdio.h>  // snprintf\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n// TcpClient::TcpClient(EventLoop* loop)\n//   : loop_(loop)\n// {\n// }\n\n// TcpClient::TcpClient(EventLoop* loop, const string& host, uint16_t port)\n//   : loop_(CHECK_NOTNULL(loop)),\n//     serverAddr_(host, port)\n// {\n// }\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace detail\n{\n\nvoid removeConnection(EventLoop* loop, const TcpConnectionPtr& conn)\n{\n  loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n}\n\nvoid removeConnector(const ConnectorPtr& connector)\n{\n  //connector->\n}\n\n}  // namespace detail\n}  // namespace net\n}  // namespace muduo\n\nTcpClient::TcpClient(EventLoop* loop,\n                     const InetAddress& serverAddr,\n                     const string& nameArg)\n  : loop_(CHECK_NOTNULL(loop)),\n    connector_(new Connector(loop, serverAddr)),\n    name_(nameArg),\n    connectionCallback_(defaultConnectionCallback),\n    messageCallback_(defaultMessageCallback),\n    retry_(false),\n    connect_(true),\n    nextConnId_(1)\n{\n  connector_->setNewConnectionCallback(\n      std::bind(&TcpClient::newConnection, this, _1));\n  // FIXME setConnectFailedCallback\n  LOG_INFO << \"TcpClient::TcpClient[\" << name_\n           << \"] - connector \" << get_pointer(connector_);\n}\n\nTcpClient::~TcpClient()\n{\n  LOG_INFO << \"TcpClient::~TcpClient[\" << name_\n           << \"] - connector \" << get_pointer(connector_);\n  TcpConnectionPtr conn;\n  bool unique = false;\n  {\n    MutexLockGuard lock(mutex_);\n    unique = connection_.unique();\n    conn = connection_;\n  }\n  if (conn)\n  {\n    assert(loop_ == conn->getLoop());\n    // FIXME: not 100% safe, if we are in different thread\n    CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);\n    loop_->runInLoop(\n        std::bind(&TcpConnection::setCloseCallback, conn, cb));\n    if (unique)\n    {\n      conn->forceClose();\n    }\n  }\n  else\n  {\n    connector_->stop();\n    // FIXME: HACK\n    loop_->runAfter(1, std::bind(&detail::removeConnector, connector_));\n  }\n}\n\nvoid TcpClient::connect()\n{\n  // FIXME: check state\n  LOG_INFO << \"TcpClient::connect[\" << name_ << \"] - connecting to \"\n           << connector_->serverAddress().toIpPort();\n  connect_ = true;\n  connector_->start();\n}\n\nvoid TcpClient::disconnect()\n{\n  connect_ = false;\n\n  {\n    MutexLockGuard lock(mutex_);\n    if (connection_)\n    {\n      connection_->shutdown();\n    }\n  }\n}\n\nvoid TcpClient::stop()\n{\n  connect_ = false;\n  connector_->stop();\n}\n\nvoid TcpClient::newConnection(int sockfd)\n{\n  loop_->assertInLoopThread();\n  InetAddress peerAddr(sockets::getPeerAddr(sockfd));\n  char buf[32];\n  snprintf(buf, sizeof buf, \":%s#%d\", peerAddr.toIpPort().c_str(), nextConnId_);\n  ++nextConnId_;\n  string connName = name_ + buf;\n\n  InetAddress localAddr(sockets::getLocalAddr(sockfd));\n  // FIXME poll with zero timeout to double confirm the new connection\n  // FIXME use make_shared if necessary\n  TcpConnectionPtr conn(new TcpConnection(loop_,\n                                          connName,\n                                          sockfd,\n                                          localAddr,\n                                          peerAddr));\n\n  conn->setConnectionCallback(connectionCallback_);\n  conn->setMessageCallback(messageCallback_);\n  conn->setWriteCompleteCallback(writeCompleteCallback_);\n  conn->setCloseCallback(\n      std::bind(&TcpClient::removeConnection, this, _1)); // FIXME: unsafe\n  {\n    MutexLockGuard lock(mutex_);\n    connection_ = conn;\n  }\n  conn->connectEstablished();\n}\n\nvoid TcpClient::removeConnection(const TcpConnectionPtr& conn)\n{\n  loop_->assertInLoopThread();\n  assert(loop_ == conn->getLoop());\n\n  {\n    MutexLockGuard lock(mutex_);\n    assert(connection_ == conn);\n    connection_.reset();\n  }\n\n  loop_->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));\n  if (retry_ && connect_)\n  {\n    LOG_INFO << \"TcpClient::connect[\" << name_ << \"] - Reconnecting to \"\n             << connector_->serverAddress().toIpPort();\n    connector_->restart();\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/TcpClient.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_TCPCLIENT_H\n#define MUDUO_NET_TCPCLIENT_H\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/TcpConnection.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Connector;\ntypedef std::shared_ptr<Connector> ConnectorPtr;\n\nclass TcpClient : noncopyable\n{\n public:\n  // TcpClient(EventLoop* loop);\n  // TcpClient(EventLoop* loop, const string& host, uint16_t port);\n  TcpClient(EventLoop* loop,\n            const InetAddress& serverAddr,\n            const string& nameArg);\n  ~TcpClient();  // force out-line dtor, for std::unique_ptr members.\n\n  void connect();\n  void disconnect();\n  void stop();\n\n  TcpConnectionPtr connection() const\n  {\n    MutexLockGuard lock(mutex_);\n    return connection_;\n  }\n\n  EventLoop* getLoop() const { return loop_; }\n  bool retry() const { return retry_; }\n  void enableRetry() { retry_ = true; }\n\n  const string& name() const\n  { return name_; }\n\n  /// Set connection callback.\n  /// Not thread safe.\n  void setConnectionCallback(ConnectionCallback cb)\n  { connectionCallback_ = std::move(cb); }\n\n  /// Set message callback.\n  /// Not thread safe.\n  void setMessageCallback(MessageCallback cb)\n  { messageCallback_ = std::move(cb); }\n\n  /// Set write complete callback.\n  /// Not thread safe.\n  void setWriteCompleteCallback(WriteCompleteCallback cb)\n  { writeCompleteCallback_ = std::move(cb); }\n\n private:\n  /// Not thread safe, but in loop\n  void newConnection(int sockfd);\n  /// Not thread safe, but in loop\n  void removeConnection(const TcpConnectionPtr& conn);\n\n  EventLoop* loop_;\n  ConnectorPtr connector_; // avoid revealing Connector\n  const string name_;\n  ConnectionCallback connectionCallback_;\n  MessageCallback messageCallback_;\n  WriteCompleteCallback writeCompleteCallback_;\n  bool retry_;   // atomic\n  bool connect_; // atomic\n  // always in loop thread\n  int nextConnId_;\n  mutable MutexLock mutex_;\n  TcpConnectionPtr connection_ GUARDED_BY(mutex_);\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_TCPCLIENT_H\n"
  },
  {
    "path": "muduo/net/TcpConnection.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/TcpConnection.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/WeakCallback.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/Socket.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <errno.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn)\n{\n  LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n            << conn->peerAddress().toIpPort() << \" is \"\n            << (conn->connected() ? \"UP\" : \"DOWN\");\n  // do not call conn->forceClose(), because some users want to register message callback only.\n}\n\nvoid muduo::net::defaultMessageCallback(const TcpConnectionPtr&,\n                                        Buffer* buf,\n                                        Timestamp)\n{\n  buf->retrieveAll();\n}\n\nTcpConnection::TcpConnection(EventLoop* loop,\n                             const string& nameArg,\n                             int sockfd,\n                             const InetAddress& localAddr,\n                             const InetAddress& peerAddr)\n  : loop_(CHECK_NOTNULL(loop)),\n    name_(nameArg),\n    state_(kConnecting),\n    reading_(true),\n    socket_(new Socket(sockfd)),\n    channel_(new Channel(loop, sockfd)),\n    localAddr_(localAddr),\n    peerAddr_(peerAddr),\n    highWaterMark_(64*1024*1024)\n{\n  channel_->setReadCallback(\n      std::bind(&TcpConnection::handleRead, this, _1));\n  channel_->setWriteCallback(\n      std::bind(&TcpConnection::handleWrite, this));\n  channel_->setCloseCallback(\n      std::bind(&TcpConnection::handleClose, this));\n  channel_->setErrorCallback(\n      std::bind(&TcpConnection::handleError, this));\n  LOG_DEBUG << \"TcpConnection::ctor[\" <<  name_ << \"] at \" << this\n            << \" fd=\" << sockfd;\n  socket_->setKeepAlive(true);\n}\n\nTcpConnection::~TcpConnection()\n{\n  LOG_DEBUG << \"TcpConnection::dtor[\" <<  name_ << \"] at \" << this\n            << \" fd=\" << channel_->fd()\n            << \" state=\" << stateToString();\n  assert(state_ == kDisconnected);\n}\n\nbool TcpConnection::getTcpInfo(struct tcp_info* tcpi) const\n{\n  return socket_->getTcpInfo(tcpi);\n}\n\nstring TcpConnection::getTcpInfoString() const\n{\n  char buf[1024];\n  buf[0] = '\\0';\n  socket_->getTcpInfoString(buf, sizeof buf);\n  return buf;\n}\n\nvoid TcpConnection::send(const void* data, int len)\n{\n  send(StringPiece(static_cast<const char*>(data), len));\n}\n\nvoid TcpConnection::send(const StringPiece& message)\n{\n  if (state_ == kConnected)\n  {\n    if (loop_->isInLoopThread())\n    {\n      sendInLoop(message);\n    }\n    else\n    {\n      void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;\n      loop_->runInLoop(\n          std::bind(fp,\n                    this,     // FIXME\n                    message.as_string()));\n                    //std::forward<string>(message)));\n    }\n  }\n}\n\n// FIXME efficiency!!!\nvoid TcpConnection::send(Buffer* buf)\n{\n  if (state_ == kConnected)\n  {\n    if (loop_->isInLoopThread())\n    {\n      sendInLoop(buf->peek(), buf->readableBytes());\n      buf->retrieveAll();\n    }\n    else\n    {\n      void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;\n      loop_->runInLoop(\n          std::bind(fp,\n                    this,     // FIXME\n                    buf->retrieveAllAsString()));\n                    //std::forward<string>(message)));\n    }\n  }\n}\n\nvoid TcpConnection::sendInLoop(const StringPiece& message)\n{\n  sendInLoop(message.data(), message.size());\n}\n\nvoid TcpConnection::sendInLoop(const void* data, size_t len)\n{\n  loop_->assertInLoopThread();\n  ssize_t nwrote = 0;\n  size_t remaining = len;\n  bool faultError = false;\n  if (state_ == kDisconnected)\n  {\n    LOG_WARN << \"disconnected, give up writing\";\n    return;\n  }\n  // if no thing in output queue, try writing directly\n  if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)\n  {\n    nwrote = sockets::write(channel_->fd(), data, len);\n    if (nwrote >= 0)\n    {\n      remaining = len - nwrote;\n      if (remaining == 0 && writeCompleteCallback_)\n      {\n        loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));\n      }\n    }\n    else // nwrote < 0\n    {\n      nwrote = 0;\n      if (errno != EWOULDBLOCK)\n      {\n        LOG_SYSERR << \"TcpConnection::sendInLoop\";\n        if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?\n        {\n          faultError = true;\n        }\n      }\n    }\n  }\n\n  assert(remaining <= len);\n  if (!faultError && remaining > 0)\n  {\n    size_t oldLen = outputBuffer_.readableBytes();\n    if (oldLen + remaining >= highWaterMark_\n        && oldLen < highWaterMark_\n        && highWaterMarkCallback_)\n    {\n      loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));\n    }\n    outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);\n    if (!channel_->isWriting())\n    {\n      channel_->enableWriting();\n    }\n  }\n}\n\nvoid TcpConnection::shutdown()\n{\n  // FIXME: use compare and swap\n  if (state_ == kConnected)\n  {\n    setState(kDisconnecting);\n    // FIXME: shared_from_this()?\n    loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));\n  }\n}\n\nvoid TcpConnection::shutdownInLoop()\n{\n  loop_->assertInLoopThread();\n  if (!channel_->isWriting())\n  {\n    // we are not writing\n    socket_->shutdownWrite();\n  }\n}\n\n// void TcpConnection::shutdownAndForceCloseAfter(double seconds)\n// {\n//   // FIXME: use compare and swap\n//   if (state_ == kConnected)\n//   {\n//     setState(kDisconnecting);\n//     loop_->runInLoop(std::bind(&TcpConnection::shutdownAndForceCloseInLoop, this, seconds));\n//   }\n// }\n\n// void TcpConnection::shutdownAndForceCloseInLoop(double seconds)\n// {\n//   loop_->assertInLoopThread();\n//   if (!channel_->isWriting())\n//   {\n//     // we are not writing\n//     socket_->shutdownWrite();\n//   }\n//   loop_->runAfter(\n//       seconds,\n//       makeWeakCallback(shared_from_this(),\n//                        &TcpConnection::forceCloseInLoop));\n// }\n\nvoid TcpConnection::forceClose()\n{\n  // FIXME: use compare and swap\n  if (state_ == kConnected || state_ == kDisconnecting)\n  {\n    setState(kDisconnecting);\n    loop_->queueInLoop(std::bind(&TcpConnection::forceCloseInLoop, shared_from_this()));\n  }\n}\n\nvoid TcpConnection::forceCloseWithDelay(double seconds)\n{\n  if (state_ == kConnected || state_ == kDisconnecting)\n  {\n    setState(kDisconnecting);\n    loop_->runAfter(\n        seconds,\n        makeWeakCallback(shared_from_this(),\n                         &TcpConnection::forceClose));  // not forceCloseInLoop to avoid race condition\n  }\n}\n\nvoid TcpConnection::forceCloseInLoop()\n{\n  loop_->assertInLoopThread();\n  if (state_ == kConnected || state_ == kDisconnecting)\n  {\n    // as if we received 0 byte in handleRead();\n    handleClose();\n  }\n}\n\nconst char* TcpConnection::stateToString() const\n{\n  switch (state_)\n  {\n    case kDisconnected:\n      return \"kDisconnected\";\n    case kConnecting:\n      return \"kConnecting\";\n    case kConnected:\n      return \"kConnected\";\n    case kDisconnecting:\n      return \"kDisconnecting\";\n    default:\n      return \"unknown state\";\n  }\n}\n\nvoid TcpConnection::setTcpNoDelay(bool on)\n{\n  socket_->setTcpNoDelay(on);\n}\n\nvoid TcpConnection::startRead()\n{\n  loop_->runInLoop(std::bind(&TcpConnection::startReadInLoop, this));\n}\n\nvoid TcpConnection::startReadInLoop()\n{\n  loop_->assertInLoopThread();\n  if (!reading_ || !channel_->isReading())\n  {\n    channel_->enableReading();\n    reading_ = true;\n  }\n}\n\nvoid TcpConnection::stopRead()\n{\n  loop_->runInLoop(std::bind(&TcpConnection::stopReadInLoop, this));\n}\n\nvoid TcpConnection::stopReadInLoop()\n{\n  loop_->assertInLoopThread();\n  if (reading_ || channel_->isReading())\n  {\n    channel_->disableReading();\n    reading_ = false;\n  }\n}\n\nvoid TcpConnection::connectEstablished()\n{\n  loop_->assertInLoopThread();\n  assert(state_ == kConnecting);\n  setState(kConnected);\n  channel_->tie(shared_from_this());\n  channel_->enableReading();\n\n  connectionCallback_(shared_from_this());\n}\n\nvoid TcpConnection::connectDestroyed()\n{\n  loop_->assertInLoopThread();\n  if (state_ == kConnected)\n  {\n    setState(kDisconnected);\n    channel_->disableAll();\n\n    connectionCallback_(shared_from_this());\n  }\n  channel_->remove();\n}\n\nvoid TcpConnection::handleRead(Timestamp receiveTime)\n{\n  loop_->assertInLoopThread();\n  int savedErrno = 0;\n  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);\n  if (n > 0)\n  {\n    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);\n  }\n  else if (n == 0)\n  {\n    handleClose();\n  }\n  else\n  {\n    errno = savedErrno;\n    LOG_SYSERR << \"TcpConnection::handleRead\";\n    handleError();\n  }\n}\n\nvoid TcpConnection::handleWrite()\n{\n  loop_->assertInLoopThread();\n  if (channel_->isWriting())\n  {\n    ssize_t n = sockets::write(channel_->fd(),\n                               outputBuffer_.peek(),\n                               outputBuffer_.readableBytes());\n    if (n > 0)\n    {\n      outputBuffer_.retrieve(n);\n      if (outputBuffer_.readableBytes() == 0)\n      {\n        channel_->disableWriting();\n        if (writeCompleteCallback_)\n        {\n          loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));\n        }\n        if (state_ == kDisconnecting)\n        {\n          shutdownInLoop();\n        }\n      }\n    }\n    else\n    {\n      LOG_SYSERR << \"TcpConnection::handleWrite\";\n      // if (state_ == kDisconnecting)\n      // {\n      //   shutdownInLoop();\n      // }\n    }\n  }\n  else\n  {\n    LOG_TRACE << \"Connection fd = \" << channel_->fd()\n              << \" is down, no more writing\";\n  }\n}\n\nvoid TcpConnection::handleClose()\n{\n  loop_->assertInLoopThread();\n  LOG_TRACE << \"fd = \" << channel_->fd() << \" state = \" << stateToString();\n  assert(state_ == kConnected || state_ == kDisconnecting);\n  // we don't close fd, leave it to dtor, so we can find leaks easily.\n  setState(kDisconnected);\n  channel_->disableAll();\n\n  TcpConnectionPtr guardThis(shared_from_this());\n  connectionCallback_(guardThis);\n  // must be the last line\n  closeCallback_(guardThis);\n}\n\nvoid TcpConnection::handleError()\n{\n  int err = sockets::getSocketError(channel_->fd());\n  LOG_ERROR << \"TcpConnection::handleError [\" << name_\n            << \"] - SO_ERROR = \" << err << \" \" << strerror_tl(err);\n}\n\n"
  },
  {
    "path": "muduo/net/TcpConnection.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_TCPCONNECTION_H\n#define MUDUO_NET_TCPCONNECTION_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/Callbacks.h\"\n#include \"muduo/net/Buffer.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <memory>\n\n#include <boost/any.hpp>\n\n// struct tcp_info is in <netinet/tcp.h>\nstruct tcp_info;\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Channel;\nclass EventLoop;\nclass Socket;\n\n///\n/// TCP connection, for both client and server usage.\n///\n/// This is an interface class, so don't expose too much details.\nclass TcpConnection : noncopyable,\n                      public std::enable_shared_from_this<TcpConnection>\n{\n public:\n  /// Constructs a TcpConnection with a connected sockfd\n  ///\n  /// User should not create this object.\n  TcpConnection(EventLoop* loop,\n                const string& name,\n                int sockfd,\n                const InetAddress& localAddr,\n                const InetAddress& peerAddr);\n  ~TcpConnection();\n\n  EventLoop* getLoop() const { return loop_; }\n  const string& name() const { return name_; }\n  const InetAddress& localAddress() const { return localAddr_; }\n  const InetAddress& peerAddress() const { return peerAddr_; }\n  bool connected() const { return state_ == kConnected; }\n  bool disconnected() const { return state_ == kDisconnected; }\n  // return true if success.\n  bool getTcpInfo(struct tcp_info*) const;\n  string getTcpInfoString() const;\n\n  // void send(string&& message); // C++11\n  void send(const void* message, int len);\n  void send(const StringPiece& message);\n  // void send(Buffer&& message); // C++11\n  void send(Buffer* message);  // this one will swap data\n  void shutdown(); // NOT thread safe, no simultaneous calling\n  // void shutdownAndForceCloseAfter(double seconds); // NOT thread safe, no simultaneous calling\n  void forceClose();\n  void forceCloseWithDelay(double seconds);\n  void setTcpNoDelay(bool on);\n  // reading or not\n  void startRead();\n  void stopRead();\n  bool isReading() const { return reading_; }; // NOT thread safe, may race with start/stopReadInLoop\n\n  void setContext(const boost::any& context)\n  { context_ = context; }\n\n  const boost::any& getContext() const\n  { return context_; }\n\n  boost::any* getMutableContext()\n  { return &context_; }\n\n  void setConnectionCallback(const ConnectionCallback& cb)\n  { connectionCallback_ = cb; }\n\n  void setMessageCallback(const MessageCallback& cb)\n  { messageCallback_ = cb; }\n\n  void setWriteCompleteCallback(const WriteCompleteCallback& cb)\n  { writeCompleteCallback_ = cb; }\n\n  void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark)\n  { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }\n\n  /// Advanced interface\n  Buffer* inputBuffer()\n  { return &inputBuffer_; }\n\n  Buffer* outputBuffer()\n  { return &outputBuffer_; }\n\n  /// Internal use only.\n  void setCloseCallback(const CloseCallback& cb)\n  { closeCallback_ = cb; }\n\n  // called when TcpServer accepts a new connection\n  void connectEstablished();   // should be called only once\n  // called when TcpServer has removed me from its map\n  void connectDestroyed();  // should be called only once\n\n private:\n  enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };\n  void handleRead(Timestamp receiveTime);\n  void handleWrite();\n  void handleClose();\n  void handleError();\n  // void sendInLoop(string&& message);\n  void sendInLoop(const StringPiece& message);\n  void sendInLoop(const void* message, size_t len);\n  void shutdownInLoop();\n  // void shutdownAndForceCloseInLoop(double seconds);\n  void forceCloseInLoop();\n  void setState(StateE s) { state_ = s; }\n  const char* stateToString() const;\n  void startReadInLoop();\n  void stopReadInLoop();\n\n  EventLoop* loop_;\n  const string name_;\n  StateE state_;  // FIXME: use atomic variable\n  bool reading_;\n  // we don't expose those classes to client.\n  std::unique_ptr<Socket> socket_;\n  std::unique_ptr<Channel> channel_;\n  const InetAddress localAddr_;\n  const InetAddress peerAddr_;\n  ConnectionCallback connectionCallback_;\n  MessageCallback messageCallback_;\n  WriteCompleteCallback writeCompleteCallback_;\n  HighWaterMarkCallback highWaterMarkCallback_;\n  CloseCallback closeCallback_;\n  size_t highWaterMark_;\n  Buffer inputBuffer_;\n  Buffer outputBuffer_; // FIXME: use list<Buffer> as output buffer.\n  boost::any context_;\n  // FIXME: creationTime_, lastReceiveTime_\n  //        bytesReceived_, bytesSent_\n};\n\ntypedef std::shared_ptr<TcpConnection> TcpConnectionPtr;\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_TCPCONNECTION_H\n"
  },
  {
    "path": "muduo/net/TcpServer.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Acceptor.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/SocketsOps.h\"\n\n#include <stdio.h>  // snprintf\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nTcpServer::TcpServer(EventLoop* loop,\n                     const InetAddress& listenAddr,\n                     const string& nameArg,\n                     Option option)\n  : loop_(CHECK_NOTNULL(loop)),\n    ipPort_(listenAddr.toIpPort()),\n    name_(nameArg),\n    acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),\n    threadPool_(new EventLoopThreadPool(loop, name_)),\n    connectionCallback_(defaultConnectionCallback),\n    messageCallback_(defaultMessageCallback),\n    nextConnId_(1)\n{\n  acceptor_->setNewConnectionCallback(\n      std::bind(&TcpServer::newConnection, this, _1, _2));\n}\n\nTcpServer::~TcpServer()\n{\n  loop_->assertInLoopThread();\n  LOG_TRACE << \"TcpServer::~TcpServer [\" << name_ << \"] destructing\";\n\n  for (auto& item : connections_)\n  {\n    TcpConnectionPtr conn(item.second);\n    item.second.reset();\n    conn->getLoop()->runInLoop(\n      std::bind(&TcpConnection::connectDestroyed, conn));\n  }\n}\n\nvoid TcpServer::setThreadNum(int numThreads)\n{\n  assert(0 <= numThreads);\n  threadPool_->setThreadNum(numThreads);\n}\n\nvoid TcpServer::start()\n{\n  if (started_.getAndSet(1) == 0)\n  {\n    threadPool_->start(threadInitCallback_);\n\n    assert(!acceptor_->listening());\n    loop_->runInLoop(\n        std::bind(&Acceptor::listen, get_pointer(acceptor_)));\n  }\n}\n\nvoid TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)\n{\n  loop_->assertInLoopThread();\n  EventLoop* ioLoop = threadPool_->getNextLoop();\n  char buf[64];\n  snprintf(buf, sizeof buf, \"-%s#%d\", ipPort_.c_str(), nextConnId_);\n  ++nextConnId_;\n  string connName = name_ + buf;\n\n  LOG_INFO << \"TcpServer::newConnection [\" << name_\n           << \"] - new connection [\" << connName\n           << \"] from \" << peerAddr.toIpPort();\n  InetAddress localAddr(sockets::getLocalAddr(sockfd));\n  // FIXME poll with zero timeout to double confirm the new connection\n  // FIXME use make_shared if necessary\n  TcpConnectionPtr conn(new TcpConnection(ioLoop,\n                                          connName,\n                                          sockfd,\n                                          localAddr,\n                                          peerAddr));\n  connections_[connName] = conn;\n  conn->setConnectionCallback(connectionCallback_);\n  conn->setMessageCallback(messageCallback_);\n  conn->setWriteCompleteCallback(writeCompleteCallback_);\n  conn->setCloseCallback(\n      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe\n  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));\n}\n\nvoid TcpServer::removeConnection(const TcpConnectionPtr& conn)\n{\n  // FIXME: unsafe\n  loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));\n}\n\nvoid TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)\n{\n  loop_->assertInLoopThread();\n  LOG_INFO << \"TcpServer::removeConnectionInLoop [\" << name_\n           << \"] - connection \" << conn->name();\n  size_t n = connections_.erase(conn->name());\n  (void)n;\n  assert(n == 1);\n  EventLoop* ioLoop = conn->getLoop();\n  ioLoop->queueInLoop(\n      std::bind(&TcpConnection::connectDestroyed, conn));\n}\n\n"
  },
  {
    "path": "muduo/net/TcpServer.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_TCPSERVER_H\n#define MUDUO_NET_TCPSERVER_H\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/TcpConnection.h\"\n\n#include <map>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Acceptor;\nclass EventLoop;\nclass EventLoopThreadPool;\n\n///\n/// TCP server, supports single-threaded and thread-pool models.\n///\n/// This is an interface class, so don't expose too much details.\nclass TcpServer : noncopyable\n{\n public:\n  typedef std::function<void(EventLoop*)> ThreadInitCallback;\n  enum Option\n  {\n    kNoReusePort,\n    kReusePort,\n  };\n\n  //TcpServer(EventLoop* loop, const InetAddress& listenAddr);\n  TcpServer(EventLoop* loop,\n            const InetAddress& listenAddr,\n            const string& nameArg,\n            Option option = kNoReusePort);\n  ~TcpServer();  // force out-line dtor, for std::unique_ptr members.\n\n  const string& ipPort() const { return ipPort_; }\n  const string& name() const { return name_; }\n  EventLoop* getLoop() const { return loop_; }\n\n  /// Set the number of threads for handling input.\n  ///\n  /// Always accepts new connection in loop's thread.\n  /// Must be called before @c start\n  /// @param numThreads\n  /// - 0 means all I/O in loop's thread, no thread will created.\n  ///   this is the default value.\n  /// - 1 means all I/O in another thread.\n  /// - N means a thread pool with N threads, new connections\n  ///   are assigned on a round-robin basis.\n  void setThreadNum(int numThreads);\n  void setThreadInitCallback(const ThreadInitCallback& cb)\n  { threadInitCallback_ = cb; }\n  /// valid after calling start()\n  std::shared_ptr<EventLoopThreadPool> threadPool()\n  { return threadPool_; }\n\n  /// Starts the server if it's not listening.\n  ///\n  /// It's harmless to call it multiple times.\n  /// Thread safe.\n  void start();\n\n  /// Set connection callback.\n  /// Not thread safe.\n  void setConnectionCallback(const ConnectionCallback& cb)\n  { connectionCallback_ = cb; }\n\n  /// Set message callback.\n  /// Not thread safe.\n  void setMessageCallback(const MessageCallback& cb)\n  { messageCallback_ = cb; }\n\n  /// Set write complete callback.\n  /// Not thread safe.\n  void setWriteCompleteCallback(const WriteCompleteCallback& cb)\n  { writeCompleteCallback_ = cb; }\n\n private:\n  /// Not thread safe, but in loop\n  void newConnection(int sockfd, const InetAddress& peerAddr);\n  /// Thread safe.\n  void removeConnection(const TcpConnectionPtr& conn);\n  /// Not thread safe, but in loop\n  void removeConnectionInLoop(const TcpConnectionPtr& conn);\n\n  typedef std::map<string, TcpConnectionPtr> ConnectionMap;\n\n  EventLoop* loop_;  // the acceptor loop\n  const string ipPort_;\n  const string name_;\n  std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor\n  std::shared_ptr<EventLoopThreadPool> threadPool_;\n  ConnectionCallback connectionCallback_;\n  MessageCallback messageCallback_;\n  WriteCompleteCallback writeCompleteCallback_;\n  ThreadInitCallback threadInitCallback_;\n  AtomicInt32 started_;\n  // always in loop thread\n  int nextConnId_;\n  ConnectionMap connections_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_TCPSERVER_H\n"
  },
  {
    "path": "muduo/net/Timer.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/Timer.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nAtomicInt64 Timer::s_numCreated_;\n\nvoid Timer::restart(Timestamp now)\n{\n  if (repeat_)\n  {\n    expiration_ = addTime(now, interval_);\n  }\n  else\n  {\n    expiration_ = Timestamp::invalid();\n  }\n}\n"
  },
  {
    "path": "muduo/net/Timer.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_TIMER_H\n#define MUDUO_NET_TIMER_H\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/Callbacks.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\n///\n/// Internal class for timer event.\n///\nclass Timer : noncopyable\n{\n public:\n  Timer(TimerCallback cb, Timestamp when, double interval)\n    : callback_(std::move(cb)),\n      expiration_(when),\n      interval_(interval),\n      repeat_(interval > 0.0),\n      sequence_(s_numCreated_.incrementAndGet())\n  { }\n\n  void run() const\n  {\n    callback_();\n  }\n\n  Timestamp expiration() const  { return expiration_; }\n  bool repeat() const { return repeat_; }\n  int64_t sequence() const { return sequence_; }\n\n  void restart(Timestamp now);\n\n  static int64_t numCreated() { return s_numCreated_.get(); }\n\n private:\n  const TimerCallback callback_;\n  Timestamp expiration_;\n  const double interval_;\n  const bool repeat_;\n  const int64_t sequence_;\n\n  static AtomicInt64 s_numCreated_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_TIMER_H\n"
  },
  {
    "path": "muduo/net/TimerId.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_TIMERID_H\n#define MUDUO_NET_TIMERID_H\n\n#include \"muduo/base/copyable.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Timer;\n\n///\n/// An opaque identifier, for canceling Timer.\n///\nclass TimerId : public muduo::copyable\n{\n public:\n  TimerId()\n    : timer_(NULL),\n      sequence_(0)\n  {\n  }\n\n  TimerId(Timer* timer, int64_t seq)\n    : timer_(timer),\n      sequence_(seq)\n  {\n  }\n\n  // default copy-ctor, dtor and assignment are okay\n\n  friend class TimerQueue;\n\n private:\n  Timer* timer_;\n  int64_t sequence_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_TIMERID_H\n"
  },
  {
    "path": "muduo/net/TimerQueue.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#ifndef __STDC_LIMIT_MACROS\n#define __STDC_LIMIT_MACROS\n#endif\n\n#include \"muduo/net/TimerQueue.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/Timer.h\"\n#include \"muduo/net/TimerId.h\"\n\n#include <sys/timerfd.h>\n#include <unistd.h>\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace detail\n{\n\nint createTimerfd()\n{\n  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,\n                                 TFD_NONBLOCK | TFD_CLOEXEC);\n  if (timerfd < 0)\n  {\n    LOG_SYSFATAL << \"Failed in timerfd_create\";\n  }\n  return timerfd;\n}\n\nstruct timespec howMuchTimeFromNow(Timestamp when)\n{\n  int64_t microseconds = when.microSecondsSinceEpoch()\n                         - Timestamp::now().microSecondsSinceEpoch();\n  if (microseconds < 100)\n  {\n    microseconds = 100;\n  }\n  struct timespec ts;\n  ts.tv_sec = static_cast<time_t>(\n      microseconds / Timestamp::kMicroSecondsPerSecond);\n  ts.tv_nsec = static_cast<long>(\n      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);\n  return ts;\n}\n\nvoid readTimerfd(int timerfd, Timestamp now)\n{\n  uint64_t howmany;\n  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);\n  LOG_TRACE << \"TimerQueue::handleRead() \" << howmany << \" at \" << now.toString();\n  if (n != sizeof howmany)\n  {\n    LOG_ERROR << \"TimerQueue::handleRead() reads \" << n << \" bytes instead of 8\";\n  }\n}\n\nvoid resetTimerfd(int timerfd, Timestamp expiration)\n{\n  // wake up loop by timerfd_settime()\n  struct itimerspec newValue;\n  struct itimerspec oldValue;\n  memZero(&newValue, sizeof newValue);\n  memZero(&oldValue, sizeof oldValue);\n  newValue.it_value = howMuchTimeFromNow(expiration);\n  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);\n  if (ret)\n  {\n    LOG_SYSERR << \"timerfd_settime()\";\n  }\n}\n\n}  // namespace detail\n}  // namespace net\n}  // namespace muduo\n\nusing namespace muduo;\nusing namespace muduo::net;\nusing namespace muduo::net::detail;\n\nTimerQueue::TimerQueue(EventLoop* loop)\n  : loop_(loop),\n    timerfd_(createTimerfd()),\n    timerfdChannel_(loop, timerfd_),\n    timers_(),\n    callingExpiredTimers_(false)\n{\n  timerfdChannel_.setReadCallback(\n      std::bind(&TimerQueue::handleRead, this));\n  // we are always reading the timerfd, we disarm it with timerfd_settime.\n  timerfdChannel_.enableReading();\n}\n\nTimerQueue::~TimerQueue()\n{\n  timerfdChannel_.disableAll();\n  timerfdChannel_.remove();\n  ::close(timerfd_);\n  // do not remove channel, since we're in EventLoop::dtor();\n  for (const Entry& timer : timers_)\n  {\n    delete timer.second;\n  }\n}\n\nTimerId TimerQueue::addTimer(TimerCallback cb,\n                             Timestamp when,\n                             double interval)\n{\n  Timer* timer = new Timer(std::move(cb), when, interval);\n  loop_->runInLoop(\n      std::bind(&TimerQueue::addTimerInLoop, this, timer));\n  return TimerId(timer, timer->sequence());\n}\n\nvoid TimerQueue::cancel(TimerId timerId)\n{\n  loop_->runInLoop(\n      std::bind(&TimerQueue::cancelInLoop, this, timerId));\n}\n\nvoid TimerQueue::addTimerInLoop(Timer* timer)\n{\n  loop_->assertInLoopThread();\n  bool earliestChanged = insert(timer);\n\n  if (earliestChanged)\n  {\n    resetTimerfd(timerfd_, timer->expiration());\n  }\n}\n\nvoid TimerQueue::cancelInLoop(TimerId timerId)\n{\n  loop_->assertInLoopThread();\n  assert(timers_.size() == activeTimers_.size());\n  ActiveTimer timer(timerId.timer_, timerId.sequence_);\n  ActiveTimerSet::iterator it = activeTimers_.find(timer);\n  if (it != activeTimers_.end())\n  {\n    size_t n = timers_.erase(Entry(it->first->expiration(), it->first));\n    assert(n == 1); (void)n;\n    delete it->first; // FIXME: no delete please\n    activeTimers_.erase(it);\n  }\n  else if (callingExpiredTimers_)\n  {\n    cancelingTimers_.insert(timer);\n  }\n  assert(timers_.size() == activeTimers_.size());\n}\n\nvoid TimerQueue::handleRead()\n{\n  loop_->assertInLoopThread();\n  Timestamp now(Timestamp::now());\n  readTimerfd(timerfd_, now);\n\n  std::vector<Entry> expired = getExpired(now);\n\n  callingExpiredTimers_ = true;\n  cancelingTimers_.clear();\n  // safe to callback outside critical section\n  for (const Entry& it : expired)\n  {\n    it.second->run();\n  }\n  callingExpiredTimers_ = false;\n\n  reset(expired, now);\n}\n\nstd::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)\n{\n  assert(timers_.size() == activeTimers_.size());\n  std::vector<Entry> expired;\n  Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));\n  TimerList::iterator end = timers_.lower_bound(sentry);\n  assert(end == timers_.end() || now < end->first);\n  std::copy(timers_.begin(), end, back_inserter(expired));\n  timers_.erase(timers_.begin(), end);\n\n  for (const Entry& it : expired)\n  {\n    ActiveTimer timer(it.second, it.second->sequence());\n    size_t n = activeTimers_.erase(timer);\n    assert(n == 1); (void)n;\n  }\n\n  assert(timers_.size() == activeTimers_.size());\n  return expired;\n}\n\nvoid TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)\n{\n  Timestamp nextExpire;\n\n  for (const Entry& it : expired)\n  {\n    ActiveTimer timer(it.second, it.second->sequence());\n    if (it.second->repeat()\n        && cancelingTimers_.find(timer) == cancelingTimers_.end())\n    {\n      it.second->restart(now);\n      insert(it.second);\n    }\n    else\n    {\n      // FIXME move to a free list\n      delete it.second; // FIXME: no delete please\n    }\n  }\n\n  if (!timers_.empty())\n  {\n    nextExpire = timers_.begin()->second->expiration();\n  }\n\n  if (nextExpire.valid())\n  {\n    resetTimerfd(timerfd_, nextExpire);\n  }\n}\n\nbool TimerQueue::insert(Timer* timer)\n{\n  loop_->assertInLoopThread();\n  assert(timers_.size() == activeTimers_.size());\n  bool earliestChanged = false;\n  Timestamp when = timer->expiration();\n  TimerList::iterator it = timers_.begin();\n  if (it == timers_.end() || when < it->first)\n  {\n    earliestChanged = true;\n  }\n  {\n    std::pair<TimerList::iterator, bool> result\n      = timers_.insert(Entry(when, timer));\n    assert(result.second); (void)result;\n  }\n  {\n    std::pair<ActiveTimerSet::iterator, bool> result\n      = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));\n    assert(result.second); (void)result;\n  }\n\n  assert(timers_.size() == activeTimers_.size());\n  return earliestChanged;\n}\n\n"
  },
  {
    "path": "muduo/net/TimerQueue.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_TIMERQUEUE_H\n#define MUDUO_NET_TIMERQUEUE_H\n\n#include <set>\n#include <vector>\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/Callbacks.h\"\n#include \"muduo/net/Channel.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass EventLoop;\nclass Timer;\nclass TimerId;\n\n///\n/// A best efforts timer queue.\n/// No guarantee that the callback will be on time.\n///\nclass TimerQueue : noncopyable\n{\n public:\n  explicit TimerQueue(EventLoop* loop);\n  ~TimerQueue();\n\n  ///\n  /// Schedules the callback to be run at given time,\n  /// repeats if @c interval > 0.0.\n  ///\n  /// Must be thread safe. Usually be called from other threads.\n  TimerId addTimer(TimerCallback cb,\n                   Timestamp when,\n                   double interval);\n\n  void cancel(TimerId timerId);\n\n private:\n\n  // FIXME: use unique_ptr<Timer> instead of raw pointers.\n  // This requires heterogeneous comparison lookup (N3465) from C++14\n  // so that we can find an T* in a set<unique_ptr<T>>.\n  typedef std::pair<Timestamp, Timer*> Entry;\n  typedef std::set<Entry> TimerList;\n  typedef std::pair<Timer*, int64_t> ActiveTimer;\n  typedef std::set<ActiveTimer> ActiveTimerSet;\n\n  void addTimerInLoop(Timer* timer);\n  void cancelInLoop(TimerId timerId);\n  // called when timerfd alarms\n  void handleRead();\n  // move out all expired timers\n  std::vector<Entry> getExpired(Timestamp now);\n  void reset(const std::vector<Entry>& expired, Timestamp now);\n\n  bool insert(Timer* timer);\n\n  EventLoop* loop_;\n  const int timerfd_;\n  Channel timerfdChannel_;\n  // Timer list sorted by expiration\n  TimerList timers_;\n\n  // for cancel()\n  ActiveTimerSet activeTimers_;\n  bool callingExpiredTimers_; /* atomic */\n  ActiveTimerSet cancelingTimers_;\n};\n\n}  // namespace net\n}  // namespace muduo\n#endif  // MUDUO_NET_TIMERQUEUE_H\n"
  },
  {
    "path": "muduo/net/ZlibStream.h",
    "content": "#pragma once\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/net/Buffer.h\"\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n#include <zlib.h>\n\nnamespace muduo\n{\nnamespace net\n{\n\n// input is zlib compressed data, output uncompressed data\n// FIXME: finish this\nclass ZlibInputStream : noncopyable\n{\n public:\n  explicit ZlibInputStream(Buffer* output)\n    : output_(output),\n      zerror_(Z_OK)\n  {\n    memZero(&zstream_, sizeof zstream_);\n    zerror_ = inflateInit(&zstream_);\n  }\n\n  ~ZlibInputStream()\n  {\n    finish();\n  }\n\n  bool write(StringPiece buf);\n  bool write(Buffer* input);\n  bool finish();\n    // inflateEnd(&zstream_);\n\n private:\n  int decompress(int flush);\n\n  Buffer* output_;\n  z_stream zstream_;\n  int zerror_;\n};\n\n// input is uncompressed data, output zlib compressed data\nclass ZlibOutputStream : noncopyable\n{\n public:\n  explicit ZlibOutputStream(Buffer* output)\n    : output_(output),\n      zerror_(Z_OK),\n      bufferSize_(1024)\n  {\n    memZero(&zstream_, sizeof zstream_);\n    zerror_ = deflateInit(&zstream_, Z_DEFAULT_COMPRESSION);\n  }\n\n  ~ZlibOutputStream()\n  {\n    finish();\n  }\n\n  // Return last error message or NULL if no error.\n  const char* zlibErrorMessage() const { return zstream_.msg; }\n\n  int zlibErrorCode() const { return zerror_; }\n  int64_t inputBytes() const { return zstream_.total_in; }\n  int64_t outputBytes() const { return zstream_.total_out; }\n  int internalOutputBufferSize() const { return bufferSize_; }\n\n  bool write(StringPiece buf)\n  {\n    if (zerror_ != Z_OK)\n      return false;\n\n    assert(zstream_.next_in == NULL && zstream_.avail_in == 0);\n    void* in = const_cast<char*>(buf.data());\n    zstream_.next_in = static_cast<Bytef*>(in);\n    zstream_.avail_in = buf.size();\n    while (zstream_.avail_in > 0 && zerror_ == Z_OK)\n    {\n      zerror_ = compress(Z_NO_FLUSH);\n    }\n    if (zstream_.avail_in == 0)\n    {\n      assert(static_cast<const void*>(zstream_.next_in) == buf.end());\n      zstream_.next_in = NULL;\n    }\n    return zerror_ == Z_OK;\n  }\n\n  // compress input as much as possible, not guarantee consuming all data.\n  bool write(Buffer* input)\n  {\n    if (zerror_ != Z_OK)\n      return false;\n\n    void* in = const_cast<char*>(input->peek());\n    zstream_.next_in = static_cast<Bytef*>(in);\n    zstream_.avail_in = static_cast<int>(input->readableBytes());\n    if (zstream_.avail_in > 0 && zerror_ == Z_OK)\n    {\n      zerror_ = compress(Z_NO_FLUSH);\n    }\n    input->retrieve(input->readableBytes() - zstream_.avail_in);\n    return zerror_ == Z_OK;\n  }\n\n  bool finish()\n  {\n    if (zerror_ != Z_OK)\n      return false;\n\n    while (zerror_ == Z_OK)\n    {\n      zerror_ = compress(Z_FINISH);\n    }\n    zerror_ = deflateEnd(&zstream_);\n    bool ok = zerror_ == Z_OK;\n    zerror_ = Z_STREAM_END;\n    return ok;\n  }\n\n private:\n  int compress(int flush)\n  {\n    output_->ensureWritableBytes(bufferSize_);\n    zstream_.next_out = reinterpret_cast<Bytef*>(output_->beginWrite());\n    zstream_.avail_out = static_cast<int>(output_->writableBytes());\n    int error = ::deflate(&zstream_, flush);\n    output_->hasWritten(output_->writableBytes() - zstream_.avail_out);\n    if (output_->writableBytes() == 0 && bufferSize_ < 65536)\n    {\n      bufferSize_ *= 2;\n    }\n    return error;\n  }\n\n  Buffer* output_;\n  z_stream zstream_;\n  int zerror_;\n  int bufferSize_;\n};\n\n}  // namespace net\n}  // namespace muduo\n"
  },
  {
    "path": "muduo/net/boilerplate.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/BoilerPlate.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n\n"
  },
  {
    "path": "muduo/net/boilerplate.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_BOILERPLATE_H\n#define MUDUO_NET_BOILERPLATE_H\n\n#include \"muduo/base/noncopyable.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass BoilerPlate : noncopyable\n{\n public:\n\n private:\n};\n\n}\n}\n\n#endif  // MUDUO_NET_BOILERPLATE_H\n"
  },
  {
    "path": "muduo/net/http/BUILD.bazel",
    "content": "cc_library(\n    name = \"http\",\n    srcs = glob([\"*.cc\"]),\n    hdrs = glob([\"*.h\"]),\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//muduo/net\",\n    ],\n)\n"
  },
  {
    "path": "muduo/net/http/CMakeLists.txt",
    "content": "set(http_SRCS\n  HttpServer.cc\n  HttpResponse.cc\n  HttpContext.cc\n  )\n\nadd_library(muduo_http ${http_SRCS})\ntarget_link_libraries(muduo_http muduo_net)\n\ninstall(TARGETS muduo_http DESTINATION lib)\nset(HEADERS\n  HttpContext.h\n  HttpRequest.h\n  HttpResponse.h\n  HttpServer.h\n  )\ninstall(FILES ${HEADERS} DESTINATION include/muduo/net/http)\n\nif(MUDUO_BUILD_EXAMPLES)\nadd_executable(httpserver_test tests/HttpServer_test.cc)\ntarget_link_libraries(httpserver_test muduo_http)\n\nif(BOOSTTEST_LIBRARY)\nadd_executable(httprequest_unittest tests/HttpRequest_unittest.cc)\ntarget_link_libraries(httprequest_unittest muduo_http boost_unit_test_framework)\nendif()\n\nendif()\n\n# add_subdirectory(tests)\n"
  },
  {
    "path": "muduo/net/http/HttpContext.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/Buffer.h\"\n#include \"muduo/net/http/HttpContext.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nbool HttpContext::processRequestLine(const char* begin, const char* end)\n{\n  bool succeed = false;\n  const char* start = begin;\n  const char* space = std::find(start, end, ' ');\n  if (space != end && request_.setMethod(start, space))\n  {\n    start = space+1;\n    space = std::find(start, end, ' ');\n    if (space != end)\n    {\n      const char* question = std::find(start, space, '?');\n      if (question != space)\n      {\n        request_.setPath(start, question);\n        request_.setQuery(question, space);\n      }\n      else\n      {\n        request_.setPath(start, space);\n      }\n      start = space+1;\n      succeed = end-start == 8 && std::equal(start, end-1, \"HTTP/1.\");\n      if (succeed)\n      {\n        if (*(end-1) == '1')\n        {\n          request_.setVersion(HttpRequest::kHttp11);\n        }\n        else if (*(end-1) == '0')\n        {\n          request_.setVersion(HttpRequest::kHttp10);\n        }\n        else\n        {\n          succeed = false;\n        }\n      }\n    }\n  }\n  return succeed;\n}\n\n// return false if any error\nbool HttpContext::parseRequest(Buffer* buf, Timestamp receiveTime)\n{\n  bool ok = true;\n  bool hasMore = true;\n  while (hasMore)\n  {\n    if (state_ == kExpectRequestLine)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        ok = processRequestLine(buf->peek(), crlf);\n        if (ok)\n        {\n          request_.setReceiveTime(receiveTime);\n          buf->retrieveUntil(crlf + 2);\n          state_ = kExpectHeaders;\n        }\n        else\n        {\n          hasMore = false;\n        }\n      }\n      else\n      {\n        hasMore = false;\n      }\n    }\n    else if (state_ == kExpectHeaders)\n    {\n      const char* crlf = buf->findCRLF();\n      if (crlf)\n      {\n        const char* colon = std::find(buf->peek(), crlf, ':');\n        if (colon != crlf)\n        {\n          request_.addHeader(buf->peek(), colon, crlf);\n        }\n        else\n        {\n          // empty line, end of header\n          // FIXME:\n          state_ = kGotAll;\n          hasMore = false;\n        }\n        buf->retrieveUntil(crlf + 2);\n      }\n      else\n      {\n        hasMore = false;\n      }\n    }\n    else if (state_ == kExpectBody)\n    {\n      // FIXME:\n    }\n  }\n  return ok;\n}\n"
  },
  {
    "path": "muduo/net/http/HttpContext.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_HTTP_HTTPCONTEXT_H\n#define MUDUO_NET_HTTP_HTTPCONTEXT_H\n\n#include \"muduo/base/copyable.h\"\n\n#include \"muduo/net/http/HttpRequest.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Buffer;\n\nclass HttpContext : public muduo::copyable\n{\n public:\n  enum HttpRequestParseState\n  {\n    kExpectRequestLine,\n    kExpectHeaders,\n    kExpectBody,\n    kGotAll,\n  };\n\n  HttpContext()\n    : state_(kExpectRequestLine)\n  {\n  }\n\n  // default copy-ctor, dtor and assignment are fine\n\n  // return false if any error\n  bool parseRequest(Buffer* buf, Timestamp receiveTime);\n\n  bool gotAll() const\n  { return state_ == kGotAll; }\n\n  void reset()\n  {\n    state_ = kExpectRequestLine;\n    HttpRequest dummy;\n    request_.swap(dummy);\n  }\n\n  const HttpRequest& request() const\n  { return request_; }\n\n  HttpRequest& request()\n  { return request_; }\n\n private:\n  bool processRequestLine(const char* begin, const char* end);\n\n  HttpRequestParseState state_;\n  HttpRequest request_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_HTTP_HTTPCONTEXT_H\n"
  },
  {
    "path": "muduo/net/http/HttpRequest.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_HTTP_HTTPREQUEST_H\n#define MUDUO_NET_HTTP_HTTPREQUEST_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/base/Types.h\"\n\n#include <map>\n#include <assert.h>\n#include <stdio.h>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass HttpRequest : public muduo::copyable\n{\n public:\n  enum Method\n  {\n    kInvalid, kGet, kPost, kHead, kPut, kDelete\n  };\n  enum Version\n  {\n    kUnknown, kHttp10, kHttp11\n  };\n\n  HttpRequest()\n    : method_(kInvalid),\n      version_(kUnknown)\n  {\n  }\n\n  void setVersion(Version v)\n  {\n    version_ = v;\n  }\n\n  Version getVersion() const\n  { return version_; }\n\n  bool setMethod(const char* start, const char* end)\n  {\n    assert(method_ == kInvalid);\n    string m(start, end);\n    if (m == \"GET\")\n    {\n      method_ = kGet;\n    }\n    else if (m == \"POST\")\n    {\n      method_ = kPost;\n    }\n    else if (m == \"HEAD\")\n    {\n      method_ = kHead;\n    }\n    else if (m == \"PUT\")\n    {\n      method_ = kPut;\n    }\n    else if (m == \"DELETE\")\n    {\n      method_ = kDelete;\n    }\n    else\n    {\n      method_ = kInvalid;\n    }\n    return method_ != kInvalid;\n  }\n\n  Method method() const\n  { return method_; }\n\n  const char* methodString() const\n  {\n    const char* result = \"UNKNOWN\";\n    switch(method_)\n    {\n      case kGet:\n        result = \"GET\";\n        break;\n      case kPost:\n        result = \"POST\";\n        break;\n      case kHead:\n        result = \"HEAD\";\n        break;\n      case kPut:\n        result = \"PUT\";\n        break;\n      case kDelete:\n        result = \"DELETE\";\n        break;\n      default:\n        break;\n    }\n    return result;\n  }\n\n  void setPath(const char* start, const char* end)\n  {\n    path_.assign(start, end);\n  }\n\n  const string& path() const\n  { return path_; }\n\n  void setQuery(const char* start, const char* end)\n  {\n    query_.assign(start, end);\n  }\n\n  const string& query() const\n  { return query_; }\n\n  void setReceiveTime(Timestamp t)\n  { receiveTime_ = t; }\n\n  Timestamp receiveTime() const\n  { return receiveTime_; }\n\n  void addHeader(const char* start, const char* colon, const char* end)\n  {\n    string field(start, colon);\n    ++colon;\n    while (colon < end && isspace(*colon))\n    {\n      ++colon;\n    }\n    string value(colon, end);\n    while (!value.empty() && isspace(value[value.size()-1]))\n    {\n      value.resize(value.size()-1);\n    }\n    headers_[field] = value;\n  }\n\n  string getHeader(const string& field) const\n  {\n    string result;\n    std::map<string, string>::const_iterator it = headers_.find(field);\n    if (it != headers_.end())\n    {\n      result = it->second;\n    }\n    return result;\n  }\n\n  const std::map<string, string>& headers() const\n  { return headers_; }\n\n  void swap(HttpRequest& that)\n  {\n    std::swap(method_, that.method_);\n    std::swap(version_, that.version_);\n    path_.swap(that.path_);\n    query_.swap(that.query_);\n    receiveTime_.swap(that.receiveTime_);\n    headers_.swap(that.headers_);\n  }\n\n private:\n  Method method_;\n  Version version_;\n  string path_;\n  string query_;\n  Timestamp receiveTime_;\n  std::map<string, string> headers_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_HTTP_HTTPREQUEST_H\n"
  },
  {
    "path": "muduo/net/http/HttpResponse.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/http/HttpResponse.h\"\n#include \"muduo/net/Buffer.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid HttpResponse::appendToBuffer(Buffer* output) const\n{\n  char buf[32];\n  snprintf(buf, sizeof buf, \"HTTP/1.1 %d \", statusCode_);\n  output->append(buf);\n  output->append(statusMessage_);\n  output->append(\"\\r\\n\");\n\n  if (closeConnection_)\n  {\n    output->append(\"Connection: close\\r\\n\");\n  }\n  else\n  {\n    snprintf(buf, sizeof buf, \"Content-Length: %zd\\r\\n\", body_.size());\n    output->append(buf);\n    output->append(\"Connection: Keep-Alive\\r\\n\");\n  }\n\n  for (const auto& header : headers_)\n  {\n    output->append(header.first);\n    output->append(\": \");\n    output->append(header.second);\n    output->append(\"\\r\\n\");\n  }\n\n  output->append(\"\\r\\n\");\n  output->append(body_);\n}\n"
  },
  {
    "path": "muduo/net/http/HttpResponse.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_HTTP_HTTPRESPONSE_H\n#define MUDUO_NET_HTTP_HTTPRESPONSE_H\n\n#include \"muduo/base/copyable.h\"\n#include \"muduo/base/Types.h\"\n\n#include <map>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Buffer;\nclass HttpResponse : public muduo::copyable\n{\n public:\n  enum HttpStatusCode\n  {\n    kUnknown,\n    k200Ok = 200,\n    k301MovedPermanently = 301,\n    k400BadRequest = 400,\n    k404NotFound = 404,\n  };\n\n  explicit HttpResponse(bool close)\n    : statusCode_(kUnknown),\n      closeConnection_(close)\n  {\n  }\n\n  void setStatusCode(HttpStatusCode code)\n  { statusCode_ = code; }\n\n  void setStatusMessage(const string& message)\n  { statusMessage_ = message; }\n\n  void setCloseConnection(bool on)\n  { closeConnection_ = on; }\n\n  bool closeConnection() const\n  { return closeConnection_; }\n\n  void setContentType(const string& contentType)\n  { addHeader(\"Content-Type\", contentType); }\n\n  // FIXME: replace string with StringPiece\n  void addHeader(const string& key, const string& value)\n  { headers_[key] = value; }\n\n  void setBody(const string& body)\n  { body_ = body; }\n\n  void appendToBuffer(Buffer* output) const;\n\n private:\n  std::map<string, string> headers_;\n  HttpStatusCode statusCode_;\n  // FIXME: add http version\n  string statusMessage_;\n  bool closeConnection_;\n  string body_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_HTTP_HTTPRESPONSE_H\n"
  },
  {
    "path": "muduo/net/http/HttpServer.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/http/HttpServer.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/http/HttpContext.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpResponse.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace detail\n{\n\nvoid defaultHttpCallback(const HttpRequest&, HttpResponse* resp)\n{\n  resp->setStatusCode(HttpResponse::k404NotFound);\n  resp->setStatusMessage(\"Not Found\");\n  resp->setCloseConnection(true);\n}\n\n}  // namespace detail\n}  // namespace net\n}  // namespace muduo\n\nHttpServer::HttpServer(EventLoop* loop,\n                       const InetAddress& listenAddr,\n                       const string& name,\n                       TcpServer::Option option)\n  : server_(loop, listenAddr, name, option),\n    httpCallback_(detail::defaultHttpCallback)\n{\n  server_.setConnectionCallback(\n      std::bind(&HttpServer::onConnection, this, _1));\n  server_.setMessageCallback(\n      std::bind(&HttpServer::onMessage, this, _1, _2, _3));\n}\n\nvoid HttpServer::start()\n{\n  LOG_WARN << \"HttpServer[\" << server_.name()\n    << \"] starts listening on \" << server_.ipPort();\n  server_.start();\n}\n\nvoid HttpServer::onConnection(const TcpConnectionPtr& conn)\n{\n  if (conn->connected())\n  {\n    conn->setContext(HttpContext());\n  }\n}\n\nvoid HttpServer::onMessage(const TcpConnectionPtr& conn,\n                           Buffer* buf,\n                           Timestamp receiveTime)\n{\n  HttpContext* context = boost::any_cast<HttpContext>(conn->getMutableContext());\n\n  if (!context->parseRequest(buf, receiveTime))\n  {\n    conn->send(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n    conn->shutdown();\n  }\n\n  if (context->gotAll())\n  {\n    onRequest(conn, context->request());\n    context->reset();\n  }\n}\n\nvoid HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)\n{\n  const string& connection = req.getHeader(\"Connection\");\n  bool close = connection == \"close\" ||\n    (req.getVersion() == HttpRequest::kHttp10 && connection != \"Keep-Alive\");\n  HttpResponse response(close);\n  httpCallback_(req, &response);\n  Buffer buf;\n  response.appendToBuffer(&buf);\n  conn->send(&buf);\n  if (response.closeConnection())\n  {\n    conn->shutdown();\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/http/HttpServer.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_HTTP_HTTPSERVER_H\n#define MUDUO_NET_HTTP_HTTPSERVER_H\n\n#include \"muduo/net/TcpServer.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass HttpRequest;\nclass HttpResponse;\n\n/// A simple embeddable HTTP server designed for report status of a program.\n/// It is not a fully HTTP 1.1 compliant server, but provides minimum features\n/// that can communicate with HttpClient and Web browser.\n/// It is synchronous, just like Java Servlet.\nclass HttpServer : noncopyable\n{\n public:\n  typedef std::function<void (const HttpRequest&,\n                              HttpResponse*)> HttpCallback;\n\n  HttpServer(EventLoop* loop,\n             const InetAddress& listenAddr,\n             const string& name,\n             TcpServer::Option option = TcpServer::kNoReusePort);\n\n  EventLoop* getLoop() const { return server_.getLoop(); }\n\n  /// Not thread safe, callback be registered before calling start().\n  void setHttpCallback(const HttpCallback& cb)\n  {\n    httpCallback_ = cb;\n  }\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void start();\n\n private:\n  void onConnection(const TcpConnectionPtr& conn);\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp receiveTime);\n  void onRequest(const TcpConnectionPtr&, const HttpRequest&);\n\n  TcpServer server_;\n  HttpCallback httpCallback_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_HTTP_HTTPSERVER_H\n"
  },
  {
    "path": "muduo/net/http/tests/HttpRequest_unittest.cc",
    "content": "#include \"muduo/net/http/HttpContext.h\"\n#include \"muduo/net/Buffer.h\"\n\n//#define BOOST_TEST_MODULE BufferTest\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\nusing muduo::string;\nusing muduo::Timestamp;\nusing muduo::net::Buffer;\nusing muduo::net::HttpContext;\nusing muduo::net::HttpRequest;\n\nBOOST_AUTO_TEST_CASE(testParseRequestAllInOne)\n{\n  HttpContext context;\n  Buffer input;\n  input.append(\"GET /index.html HTTP/1.1\\r\\n\"\n       \"Host: www.chenshuo.com\\r\\n\"\n       \"\\r\\n\");\n\n  BOOST_CHECK(context.parseRequest(&input, Timestamp::now()));\n  BOOST_CHECK(context.gotAll());\n  const HttpRequest& request = context.request();\n  BOOST_CHECK_EQUAL(request.method(), HttpRequest::kGet);\n  BOOST_CHECK_EQUAL(request.path(), string(\"/index.html\"));\n  BOOST_CHECK_EQUAL(request.getVersion(), HttpRequest::kHttp11);\n  BOOST_CHECK_EQUAL(request.getHeader(\"Host\"), string(\"www.chenshuo.com\"));\n  BOOST_CHECK_EQUAL(request.getHeader(\"User-Agent\"), string(\"\"));\n}\n\nBOOST_AUTO_TEST_CASE(testParseRequestInTwoPieces)\n{\n  string all(\"GET /index.html HTTP/1.1\\r\\n\"\n       \"Host: www.chenshuo.com\\r\\n\"\n       \"\\r\\n\");\n\n  for (size_t sz1 = 0; sz1 < all.size(); ++sz1)\n  {\n    HttpContext context;\n    Buffer input;\n    input.append(all.c_str(), sz1);\n    BOOST_CHECK(context.parseRequest(&input, Timestamp::now()));\n    BOOST_CHECK(!context.gotAll());\n\n    size_t sz2 = all.size() - sz1;\n    input.append(all.c_str() + sz1, sz2);\n    BOOST_CHECK(context.parseRequest(&input, Timestamp::now()));\n    BOOST_CHECK(context.gotAll());\n    const HttpRequest& request = context.request();\n    BOOST_CHECK_EQUAL(request.method(), HttpRequest::kGet);\n    BOOST_CHECK_EQUAL(request.path(), string(\"/index.html\"));\n    BOOST_CHECK_EQUAL(request.getVersion(), HttpRequest::kHttp11);\n    BOOST_CHECK_EQUAL(request.getHeader(\"Host\"), string(\"www.chenshuo.com\"));\n    BOOST_CHECK_EQUAL(request.getHeader(\"User-Agent\"), string(\"\"));\n  }\n}\n\nBOOST_AUTO_TEST_CASE(testParseRequestEmptyHeaderValue)\n{\n  HttpContext context;\n  Buffer input;\n  input.append(\"GET /index.html HTTP/1.1\\r\\n\"\n       \"Host: www.chenshuo.com\\r\\n\"\n       \"User-Agent:\\r\\n\"\n       \"Accept-Encoding: \\r\\n\"\n       \"\\r\\n\");\n\n  BOOST_CHECK(context.parseRequest(&input, Timestamp::now()));\n  BOOST_CHECK(context.gotAll());\n  const HttpRequest& request = context.request();\n  BOOST_CHECK_EQUAL(request.method(), HttpRequest::kGet);\n  BOOST_CHECK_EQUAL(request.path(), string(\"/index.html\"));\n  BOOST_CHECK_EQUAL(request.getVersion(), HttpRequest::kHttp11);\n  BOOST_CHECK_EQUAL(request.getHeader(\"Host\"), string(\"www.chenshuo.com\"));\n  BOOST_CHECK_EQUAL(request.getHeader(\"User-Agent\"), string(\"\"));\n  BOOST_CHECK_EQUAL(request.getHeader(\"Accept-Encoding\"), string(\"\"));\n}\n"
  },
  {
    "path": "muduo/net/http/tests/HttpServer_test.cc",
    "content": "#include \"muduo/net/http/HttpServer.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpResponse.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/base/Logging.h\"\n\n#include <iostream>\n#include <map>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nextern char favicon[555];\nbool benchmark = false;\n\nvoid onRequest(const HttpRequest& req, HttpResponse* resp)\n{\n  std::cout << \"Headers \" << req.methodString() << \" \" << req.path() << std::endl;\n  if (!benchmark)\n  {\n    const std::map<string, string>& headers = req.headers();\n    for (const auto& header : headers)\n    {\n      std::cout << header.first << \": \" << header.second << std::endl;\n    }\n  }\n\n  if (req.path() == \"/\")\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"text/html\");\n    resp->addHeader(\"Server\", \"Muduo\");\n    string now = Timestamp::now().toFormattedString();\n    resp->setBody(\"<html><head><title>This is title</title></head>\"\n        \"<body><h1>Hello</h1>Now is \" + now +\n        \"</body></html>\");\n  }\n  else if (req.path() == \"/favicon.ico\")\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"image/png\");\n    resp->setBody(string(favicon, sizeof favicon));\n  }\n  else if (req.path() == \"/hello\")\n  {\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"text/plain\");\n    resp->addHeader(\"Server\", \"Muduo\");\n    resp->setBody(\"hello, world!\\n\");\n  }\n  else\n  {\n    resp->setStatusCode(HttpResponse::k404NotFound);\n    resp->setStatusMessage(\"Not Found\");\n    resp->setCloseConnection(true);\n  }\n}\n\nint main(int argc, char* argv[])\n{\n  int numThreads = 0;\n  if (argc > 1)\n  {\n    benchmark = true;\n    Logger::setLogLevel(Logger::WARN);\n    numThreads = atoi(argv[1]);\n  }\n  EventLoop loop;\n  HttpServer server(&loop, InetAddress(8000), \"dummy\");\n  server.setHttpCallback(onRequest);\n  server.setThreadNum(numThreads);\n  server.start();\n  loop.loop();\n}\n\nchar favicon[555] = {\n  '\\x89', 'P', 'N', 'G', '\\xD', '\\xA', '\\x1A', '\\xA',\n  '\\x0', '\\x0', '\\x0', '\\xD', 'I', 'H', 'D', 'R',\n  '\\x0', '\\x0', '\\x0', '\\x10', '\\x0', '\\x0', '\\x0', '\\x10',\n  '\\x8', '\\x6', '\\x0', '\\x0', '\\x0', '\\x1F', '\\xF3', '\\xFF',\n  'a', '\\x0', '\\x0', '\\x0', '\\x19', 't', 'E', 'X',\n  't', 'S', 'o', 'f', 't', 'w', 'a', 'r',\n  'e', '\\x0', 'A', 'd', 'o', 'b', 'e', '\\x20',\n  'I', 'm', 'a', 'g', 'e', 'R', 'e', 'a',\n  'd', 'y', 'q', '\\xC9', 'e', '\\x3C', '\\x0', '\\x0',\n  '\\x1', '\\xCD', 'I', 'D', 'A', 'T', 'x', '\\xDA',\n  '\\x94', '\\x93', '9', 'H', '\\x3', 'A', '\\x14', '\\x86',\n  '\\xFF', '\\x5D', 'b', '\\xA7', '\\x4', 'R', '\\xC4', 'm',\n  '\\x22', '\\x1E', '\\xA0', 'F', '\\x24', '\\x8', '\\x16', '\\x16',\n  'v', '\\xA', '6', '\\xBA', 'J', '\\x9A', '\\x80', '\\x8',\n  'A', '\\xB4', 'q', '\\x85', 'X', '\\x89', 'G', '\\xB0',\n  'I', '\\xA9', 'Q', '\\x24', '\\xCD', '\\xA6', '\\x8', '\\xA4',\n  'H', 'c', '\\x91', 'B', '\\xB', '\\xAF', 'V', '\\xC1',\n  'F', '\\xB4', '\\x15', '\\xCF', '\\x22', 'X', '\\x98', '\\xB',\n  'T', 'H', '\\x8A', 'd', '\\x93', '\\x8D', '\\xFB', 'F',\n  'g', '\\xC9', '\\x1A', '\\x14', '\\x7D', '\\xF0', 'f', 'v',\n  'f', '\\xDF', '\\x7C', '\\xEF', '\\xE7', 'g', 'F', '\\xA8',\n  '\\xD5', 'j', 'H', '\\x24', '\\x12', '\\x2A', '\\x0', '\\x5',\n  '\\xBF', 'G', '\\xD4', '\\xEF', '\\xF7', '\\x2F', '6', '\\xEC',\n  '\\x12', '\\x20', '\\x1E', '\\x8F', '\\xD7', '\\xAA', '\\xD5', '\\xEA',\n  '\\xAF', 'I', '5', 'F', '\\xAA', 'T', '\\x5F', '\\x9F',\n  '\\x22', 'A', '\\x2A', '\\x95', '\\xA', '\\x83', '\\xE5', 'r',\n  '9', 'd', '\\xB3', 'Y', '\\x96', '\\x99', 'L', '\\x6',\n  '\\xE9', 't', '\\x9A', '\\x25', '\\x85', '\\x2C', '\\xCB', 'T',\n  '\\xA7', '\\xC4', 'b', '1', '\\xB5', '\\x5E', '\\x0', '\\x3',\n  'h', '\\x9A', '\\xC6', '\\x16', '\\x82', '\\x20', 'X', 'R',\n  '\\x14', 'E', '6', 'S', '\\x94', '\\xCB', 'e', 'x',\n  '\\xBD', '\\x5E', '\\xAA', 'U', 'T', '\\x23', 'L', '\\xC0',\n  '\\xE0', '\\xE2', '\\xC1', '\\x8F', '\\x0', '\\x9E', '\\xBC', '\\x9',\n  'A', '\\x7C', '\\x3E', '\\x1F', '\\x83', 'D', '\\x22', '\\x11',\n  '\\xD5', 'T', '\\x40', '\\x3F', '8', '\\x80', 'w', '\\xE5',\n  '3', '\\x7', '\\xB8', '\\x5C', '\\x2E', 'H', '\\x92', '\\x4',\n  '\\x87', '\\xC3', '\\x81', '\\x40', '\\x20', '\\x40', 'g', '\\x98',\n  '\\xE9', '6', '\\x1A', '\\xA6', 'g', '\\x15', '\\x4', '\\xE3',\n  '\\xD7', '\\xC8', '\\xBD', '\\x15', '\\xE1', 'i', '\\xB7', 'C',\n  '\\xAB', '\\xEA', 'x', '\\x2F', 'j', 'X', '\\x92', '\\xBB',\n  '\\x18', '\\x20', '\\x9F', '\\xCF', '3', '\\xC3', '\\xB8', '\\xE9',\n  'N', '\\xA7', '\\xD3', 'l', 'J', '\\x0', 'i', '6',\n  '\\x7C', '\\x8E', '\\xE1', '\\xFE', 'V', '\\x84', '\\xE7', '\\x3C',\n  '\\x9F', 'r', '\\x2B', '\\x3A', 'B', '\\x7B', '7', 'f',\n  'w', '\\xAE', '\\x8E', '\\xE', '\\xF3', '\\xBD', 'R', '\\xA9',\n  'd', '\\x2', 'B', '\\xAF', '\\x85', '2', 'f', 'F',\n  '\\xBA', '\\xC', '\\xD9', '\\x9F', '\\x1D', '\\x9A', 'l', '\\x22',\n  '\\xE6', '\\xC7', '\\x3A', '\\x2C', '\\x80', '\\xEF', '\\xC1', '\\x15',\n  '\\x90', '\\x7', '\\x93', '\\xA2', '\\x28', '\\xA0', 'S', 'j',\n  '\\xB1', '\\xB8', '\\xDF', '\\x29', '5', 'C', '\\xE', '\\x3F',\n  'X', '\\xFC', '\\x98', '\\xDA', 'y', 'j', 'P', '\\x40',\n  '\\x0', '\\x87', '\\xAE', '\\x1B', '\\x17', 'B', '\\xB4', '\\x3A',\n  '\\x3F', '\\xBE', 'y', '\\xC7', '\\xA', '\\x26', '\\xB6', '\\xEE',\n  '\\xD9', '\\x9A', '\\x60', '\\x14', '\\x93', '\\xDB', '\\x8F', '\\xD',\n  '\\xA', '\\x2E', '\\xE9', '\\x23', '\\x95', '\\x29', 'X', '\\x0',\n  '\\x27', '\\xEB', 'n', 'V', 'p', '\\xBC', '\\xD6', '\\xCB',\n  '\\xD6', 'G', '\\xAB', '\\x3D', 'l', '\\x7D', '\\xB8', '\\xD2',\n  '\\xDD', '\\xA0', '\\x60', '\\x83', '\\xBA', '\\xEF', '\\x5F', '\\xA4',\n  '\\xEA', '\\xCC', '\\x2', 'N', '\\xAE', '\\x5E', 'p', '\\x1A',\n  '\\xEC', '\\xB3', '\\x40', '9', '\\xAC', '\\xFE', '\\xF2', '\\x91',\n  '\\x89', 'g', '\\x91', '\\x85', '\\x21', '\\xA8', '\\x87', '\\xB7',\n  'X', '\\x7E', '\\x7E', '\\x85', '\\xBB', '\\xCD', 'N', 'N',\n  'b', 't', '\\x40', '\\xFA', '\\x93', '\\x89', '\\xEC', '\\x1E',\n  '\\xEC', '\\x86', '\\x2', 'H', '\\x26', '\\x93', '\\xD0', 'u',\n  '\\x1D', '\\x7F', '\\x9', '2', '\\x95', '\\xBF', '\\x1F', '\\xDB',\n  '\\xD7', 'c', '\\x8A', '\\x1A', '\\xF7', '\\x5C', '\\xC1', '\\xFF',\n  '\\x22', 'J', '\\xC3', '\\x87', '\\x0', '\\x3', '\\x0', 'K',\n  '\\xBB', '\\xF8', '\\xD6', '\\x2A', 'v', '\\x98', 'I', '\\x0',\n  '\\x0', '\\x0', '\\x0', 'I', 'E', 'N', 'D', '\\xAE',\n  'B', '\\x60', '\\x82',\n};\n"
  },
  {
    "path": "muduo/net/inspect/BUILD.bazel",
    "content": "cc_library(\n    name = \"inspect\",\n    srcs = glob([\"*.cc\"]),\n    hdrs = glob([\"*.h\"]),\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//muduo/net/http\",\n    ],\n)\n"
  },
  {
    "path": "muduo/net/inspect/CMakeLists.txt",
    "content": "set(inspect_SRCS\n  Inspector.cc\n  PerformanceInspector.cc\n  ProcessInspector.cc\n  SystemInspector.cc\n  )\n\nadd_library(muduo_inspect ${inspect_SRCS})\ntarget_link_libraries(muduo_inspect muduo_http)\n\nif(TCMALLOC_INCLUDE_DIR AND TCMALLOC_LIBRARY)\n  set_target_properties(muduo_inspect PROPERTIES COMPILE_FLAGS \"-DHAVE_TCMALLOC\")\n  target_link_libraries(muduo_inspect tcmalloc_and_profiler)\nendif()\n\ninstall(TARGETS muduo_inspect DESTINATION lib)\nset(HEADERS\n  Inspector.h\n  )\ninstall(FILES ${HEADERS} DESTINATION include/muduo/net/inspect)\n\nif(MUDUO_BUILD_EXAMPLES)\nadd_executable(inspector_test tests/Inspector_test.cc)\ntarget_link_libraries(inspector_test muduo_inspect)\nendif()\n\n"
  },
  {
    "path": "muduo/net/inspect/Inspector.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/inspect/Inspector.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpResponse.h\"\n#include \"muduo/net/inspect/ProcessInspector.h\"\n#include \"muduo/net/inspect/PerformanceInspector.h\"\n#include \"muduo/net/inspect/SystemInspector.h\"\n\n//#include <iostream>\n//#include <iterator>\n//#include <sstream>\n//#include <boost/algorithm/string/classification.hpp>\n//#include <boost/algorithm/string/split.hpp>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace\n{\nInspector* g_globalInspector = 0;\n\n// Looks buggy\nstd::vector<string> split(const string& str)\n{\n  std::vector<string> result;\n  size_t start = 0;\n  size_t pos = str.find('/');\n  while (pos != string::npos)\n  {\n    if (pos > start)\n    {\n      result.push_back(str.substr(start, pos-start));\n    }\n    start = pos+1;\n    pos = str.find('/', start);\n  }\n\n  if (start < str.length())\n  {\n    result.push_back(str.substr(start));\n  }\n\n  return result;\n}\n\n}  // namespace\n\nextern char favicon[1743];\n\nInspector::Inspector(EventLoop* loop,\n                     const InetAddress& httpAddr,\n                     const string& name)\n    : server_(loop, httpAddr, \"Inspector:\"+name),\n      processInspector_(new ProcessInspector),\n      systemInspector_(new SystemInspector)\n{\n  assert(CurrentThread::isMainThread());\n  assert(g_globalInspector == 0);\n  g_globalInspector = this;\n  server_.setHttpCallback(std::bind(&Inspector::onRequest, this, _1, _2));\n  processInspector_->registerCommands(this);\n  systemInspector_->registerCommands(this);\n#ifdef HAVE_TCMALLOC\n  performanceInspector_.reset(new PerformanceInspector);\n  performanceInspector_->registerCommands(this);\n#endif\n  loop->runAfter(0, std::bind(&Inspector::start, this)); // little race condition\n}\n\nInspector::~Inspector()\n{\n  assert(CurrentThread::isMainThread());\n  g_globalInspector = NULL;\n}\n\nvoid Inspector::add(const string& module,\n                    const string& command,\n                    const Callback& cb,\n                    const string& help)\n{\n  MutexLockGuard lock(mutex_);\n  modules_[module][command] = cb;\n  helps_[module][command] = help;\n}\n\nvoid Inspector::remove(const string& module, const string& command)\n{\n  MutexLockGuard lock(mutex_);\n  std::map<string, CommandList>::iterator it = modules_.find(module);\n  if (it != modules_.end())\n  {\n    it->second.erase(command);\n    helps_[module].erase(command);\n  }\n}\n\nvoid Inspector::start()\n{\n  server_.start();\n}\n\nvoid Inspector::onRequest(const HttpRequest& req, HttpResponse* resp)\n{\n  if (req.path() == \"/\")\n  {\n    string result;\n    MutexLockGuard lock(mutex_);\n    for (std::map<string, HelpList>::const_iterator helpListI = helps_.begin();\n         helpListI != helps_.end();\n         ++helpListI)\n    {\n      const HelpList& list = helpListI->second;\n      for (const auto& it : list)\n      {\n        result += \"/\";\n        result += helpListI->first;\n        result += \"/\";\n        result += it.first;\n        size_t len = helpListI->first.size() + it.first.size();\n        result += string(len >= 25 ? 1 : 25 - len, ' ');\n        result += it.second;\n        result += \"\\n\";\n      }\n    }\n    resp->setStatusCode(HttpResponse::k200Ok);\n    resp->setStatusMessage(\"OK\");\n    resp->setContentType(\"text/plain\");\n    resp->setBody(result);\n  }\n  else\n  {\n    std::vector<string> result = split(req.path());\n    // boost::split(result, req.path(), boost::is_any_of(\"/\"));\n    //std::copy(result.begin(), result.end(), std::ostream_iterator<string>(std::cout, \", \"));\n    //std::cout << \"\\n\";\n    bool ok = false;\n    if (result.size() == 0)\n    {\n      LOG_DEBUG << req.path();\n    }\n    else if (result.size() == 1)\n    {\n      string module = result[0];\n      if (module == \"favicon.ico\")\n      {\n        resp->setStatusCode(HttpResponse::k200Ok);\n        resp->setStatusMessage(\"OK\");\n        resp->setContentType(\"image/png\");\n        resp->setBody(string(favicon, sizeof favicon));\n\n        ok = true;\n      }\n      else\n      {\n        LOG_ERROR << \"Unimplemented \" << module;\n      }\n    }\n    else\n    {\n      string module = result[0];\n      MutexLockGuard lock(mutex_);\n      std::map<string, CommandList>::const_iterator commListI = modules_.find(module);\n      if (commListI != modules_.end())\n      {\n        string command = result[1];\n        const CommandList& commList = commListI->second;\n        CommandList::const_iterator it = commList.find(command);\n        if (it != commList.end())\n        {\n          ArgList args(result.begin()+2, result.end());\n          if (it->second)\n          {\n            resp->setStatusCode(HttpResponse::k200Ok);\n            resp->setStatusMessage(\"OK\");\n            resp->setContentType(\"text/plain\");\n            const Callback& cb = it->second;\n            resp->setBody(cb(req.method(), args));\n            ok = true;\n          }\n        }\n      }\n\n    }\n\n    if (!ok)\n    {\n      resp->setStatusCode(HttpResponse::k404NotFound);\n      resp->setStatusMessage(\"Not Found\");\n    }\n    //resp->setCloseConnection(true);\n  }\n}\n\nchar favicon[1743] =\n{\n  '\\x89', '\\x50', '\\x4e', '\\x47', '\\x0d', '\\x0a', '\\x1a', '\\x0a', '\\x00', '\\x00',\n  '\\x00', '\\x0d', '\\x49', '\\x48', '\\x44', '\\x52', '\\x00', '\\x00', '\\x00', '\\x10',\n  '\\x00', '\\x00', '\\x00', '\\x10', '\\x08', '\\x06', '\\x00', '\\x00', '\\x00', '\\x1f',\n  '\\xf3', '\\xff', '\\x61', '\\x00', '\\x00', '\\x00', '\\x04', '\\x73', '\\x42', '\\x49',\n  '\\x54', '\\x08', '\\x08', '\\x08', '\\x08', '\\x7c', '\\x08', '\\x64', '\\x88', '\\x00',\n  '\\x00', '\\x00', '\\x09', '\\x70', '\\x48', '\\x59', '\\x73', '\\x00', '\\x00', '\\x0b',\n  '\\x12', '\\x00', '\\x00', '\\x0b', '\\x12', '\\x01', '\\xd2', '\\xdd', '\\x7e', '\\xfc',\n  '\\x00', '\\x00', '\\x00', '\\x1c', '\\x74', '\\x45', '\\x58', '\\x74', '\\x53', '\\x6f',\n  '\\x66', '\\x74', '\\x77', '\\x61', '\\x72', '\\x65', '\\x00', '\\x41', '\\x64', '\\x6f',\n  '\\x62', '\\x65', '\\x20', '\\x46', '\\x69', '\\x72', '\\x65', '\\x77', '\\x6f', '\\x72',\n  '\\x6b', '\\x73', '\\x20', '\\x43', '\\x53', '\\x33', '\\x98', '\\xd6', '\\x46', '\\x03',\n  '\\x00', '\\x00', '\\x00', '\\x15', '\\x74', '\\x45', '\\x58', '\\x74', '\\x43', '\\x72',\n  '\\x65', '\\x61', '\\x74', '\\x69', '\\x6f', '\\x6e', '\\x20', '\\x54', '\\x69', '\\x6d',\n  '\\x65', '\\x00', '\\x32', '\\x2f', '\\x31', '\\x37', '\\x2f', '\\x30', '\\x38', '\\x20',\n  '\\x9c', '\\xaa', '\\x58', '\\x00', '\\x00', '\\x04', '\\x11', '\\x74', '\\x45', '\\x58',\n  '\\x74', '\\x58', '\\x4d', '\\x4c', '\\x3a', '\\x63', '\\x6f', '\\x6d', '\\x2e', '\\x61',\n  '\\x64', '\\x6f', '\\x62', '\\x65', '\\x2e', '\\x78', '\\x6d', '\\x70', '\\x00', '\\x3c',\n  '\\x3f', '\\x78', '\\x70', '\\x61', '\\x63', '\\x6b', '\\x65', '\\x74', '\\x20', '\\x62',\n  '\\x65', '\\x67', '\\x69', '\\x6e', '\\x3d', '\\x22', '\\x20', '\\x20', '\\x20', '\\x22',\n  '\\x20', '\\x69', '\\x64', '\\x3d', '\\x22', '\\x57', '\\x35', '\\x4d', '\\x30', '\\x4d',\n  '\\x70', '\\x43', '\\x65', '\\x68', '\\x69', '\\x48', '\\x7a', '\\x72', '\\x65', '\\x53',\n  '\\x7a', '\\x4e', '\\x54', '\\x63', '\\x7a', '\\x6b', '\\x63', '\\x39', '\\x64', '\\x22',\n  '\\x3f', '\\x3e', '\\x0a', '\\x3c', '\\x78', '\\x3a', '\\x78', '\\x6d', '\\x70', '\\x6d',\n  '\\x65', '\\x74', '\\x61', '\\x20', '\\x78', '\\x6d', '\\x6c', '\\x6e', '\\x73', '\\x3a',\n  '\\x78', '\\x3d', '\\x22', '\\x61', '\\x64', '\\x6f', '\\x62', '\\x65', '\\x3a', '\\x6e',\n  '\\x73', '\\x3a', '\\x6d', '\\x65', '\\x74', '\\x61', '\\x2f', '\\x22', '\\x20', '\\x78',\n  '\\x3a', '\\x78', '\\x6d', '\\x70', '\\x74', '\\x6b', '\\x3d', '\\x22', '\\x41', '\\x64',\n  '\\x6f', '\\x62', '\\x65', '\\x20', '\\x58', '\\x4d', '\\x50', '\\x20', '\\x43', '\\x6f',\n  '\\x72', '\\x65', '\\x20', '\\x34', '\\x2e', '\\x31', '\\x2d', '\\x63', '\\x30', '\\x33',\n  '\\x34', '\\x20', '\\x34', '\\x36', '\\x2e', '\\x32', '\\x37', '\\x32', '\\x39', '\\x37',\n  '\\x36', '\\x2c', '\\x20', '\\x53', '\\x61', '\\x74', '\\x20', '\\x4a', '\\x61', '\\x6e',\n  '\\x20', '\\x32', '\\x37', '\\x20', '\\x32', '\\x30', '\\x30', '\\x37', '\\x20', '\\x32',\n  '\\x32', '\\x3a', '\\x31', '\\x31', '\\x3a', '\\x34', '\\x31', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x22', '\\x3e', '\\x0a', '\\x20', '\\x20',\n  '\\x20', '\\x3c', '\\x72', '\\x64', '\\x66', '\\x3a', '\\x52', '\\x44', '\\x46', '\\x20',\n  '\\x78', '\\x6d', '\\x6c', '\\x6e', '\\x73', '\\x3a', '\\x72', '\\x64', '\\x66', '\\x3d',\n  '\\x22', '\\x68', '\\x74', '\\x74', '\\x70', '\\x3a', '\\x2f', '\\x2f', '\\x77', '\\x77',\n  '\\x77', '\\x2e', '\\x77', '\\x33', '\\x2e', '\\x6f', '\\x72', '\\x67', '\\x2f', '\\x31',\n  '\\x39', '\\x39', '\\x39', '\\x2f', '\\x30', '\\x32', '\\x2f', '\\x32', '\\x32', '\\x2d',\n  '\\x72', '\\x64', '\\x66', '\\x2d', '\\x73', '\\x79', '\\x6e', '\\x74', '\\x61', '\\x78',\n  '\\x2d', '\\x6e', '\\x73', '\\x23', '\\x22', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x3c', '\\x72', '\\x64', '\\x66', '\\x3a', '\\x44', '\\x65',\n  '\\x73', '\\x63', '\\x72', '\\x69', '\\x70', '\\x74', '\\x69', '\\x6f', '\\x6e', '\\x20',\n  '\\x72', '\\x64', '\\x66', '\\x3a', '\\x61', '\\x62', '\\x6f', '\\x75', '\\x74', '\\x3d',\n  '\\x22', '\\x22', '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x78', '\\x6d', '\\x6c', '\\x6e', '\\x73',\n  '\\x3a', '\\x78', '\\x61', '\\x70', '\\x3d', '\\x22', '\\x68', '\\x74', '\\x74', '\\x70',\n  '\\x3a', '\\x2f', '\\x2f', '\\x6e', '\\x73', '\\x2e', '\\x61', '\\x64', '\\x6f', '\\x62',\n  '\\x65', '\\x2e', '\\x63', '\\x6f', '\\x6d', '\\x2f', '\\x78', '\\x61', '\\x70', '\\x2f',\n  '\\x31', '\\x2e', '\\x30', '\\x2f', '\\x22', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x3c', '\\x78', '\\x61', '\\x70',\n  '\\x3a', '\\x43', '\\x72', '\\x65', '\\x61', '\\x74', '\\x6f', '\\x72', '\\x54', '\\x6f',\n  '\\x6f', '\\x6c', '\\x3e', '\\x41', '\\x64', '\\x6f', '\\x62', '\\x65', '\\x20', '\\x46',\n  '\\x69', '\\x72', '\\x65', '\\x77', '\\x6f', '\\x72', '\\x6b', '\\x73', '\\x20', '\\x43',\n  '\\x53', '\\x33', '\\x3c', '\\x2f', '\\x78', '\\x61', '\\x70', '\\x3a', '\\x43', '\\x72',\n  '\\x65', '\\x61', '\\x74', '\\x6f', '\\x72', '\\x54', '\\x6f', '\\x6f', '\\x6c', '\\x3e',\n  '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x3c', '\\x78', '\\x61', '\\x70', '\\x3a', '\\x43', '\\x72', '\\x65', '\\x61', '\\x74',\n  '\\x65', '\\x44', '\\x61', '\\x74', '\\x65', '\\x3e', '\\x32', '\\x30', '\\x30', '\\x38',\n  '\\x2d', '\\x30', '\\x32', '\\x2d', '\\x31', '\\x37', '\\x54', '\\x30', '\\x32', '\\x3a',\n  '\\x33', '\\x36', '\\x3a', '\\x34', '\\x35', '\\x5a', '\\x3c', '\\x2f', '\\x78', '\\x61',\n  '\\x70', '\\x3a', '\\x43', '\\x72', '\\x65', '\\x61', '\\x74', '\\x65', '\\x44', '\\x61',\n  '\\x74', '\\x65', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x3c', '\\x78', '\\x61', '\\x70', '\\x3a', '\\x4d', '\\x6f',\n  '\\x64', '\\x69', '\\x66', '\\x79', '\\x44', '\\x61', '\\x74', '\\x65', '\\x3e', '\\x32',\n  '\\x30', '\\x30', '\\x38', '\\x2d', '\\x30', '\\x33', '\\x2d', '\\x32', '\\x34', '\\x54',\n  '\\x31', '\\x39', '\\x3a', '\\x30', '\\x30', '\\x3a', '\\x34', '\\x32', '\\x5a', '\\x3c',\n  '\\x2f', '\\x78', '\\x61', '\\x70', '\\x3a', '\\x4d', '\\x6f', '\\x64', '\\x69', '\\x66',\n  '\\x79', '\\x44', '\\x61', '\\x74', '\\x65', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x3c', '\\x2f', '\\x72', '\\x64', '\\x66', '\\x3a', '\\x44',\n  '\\x65', '\\x73', '\\x63', '\\x72', '\\x69', '\\x70', '\\x74', '\\x69', '\\x6f', '\\x6e',\n  '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x3c', '\\x72',\n  '\\x64', '\\x66', '\\x3a', '\\x44', '\\x65', '\\x73', '\\x63', '\\x72', '\\x69', '\\x70',\n  '\\x74', '\\x69', '\\x6f', '\\x6e', '\\x20', '\\x72', '\\x64', '\\x66', '\\x3a', '\\x61',\n  '\\x62', '\\x6f', '\\x75', '\\x74', '\\x3d', '\\x22', '\\x22', '\\x0a', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x78', '\\x6d', '\\x6c', '\\x6e', '\\x73', '\\x3a', '\\x64', '\\x63', '\\x3d', '\\x22',\n  '\\x68', '\\x74', '\\x74', '\\x70', '\\x3a', '\\x2f', '\\x2f', '\\x70', '\\x75', '\\x72',\n  '\\x6c', '\\x2e', '\\x6f', '\\x72', '\\x67', '\\x2f', '\\x64', '\\x63', '\\x2f', '\\x65',\n  '\\x6c', '\\x65', '\\x6d', '\\x65', '\\x6e', '\\x74', '\\x73', '\\x2f', '\\x31', '\\x2e',\n  '\\x31', '\\x2f', '\\x22', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x3c', '\\x64', '\\x63', '\\x3a', '\\x66', '\\x6f',\n  '\\x72', '\\x6d', '\\x61', '\\x74', '\\x3e', '\\x69', '\\x6d', '\\x61', '\\x67', '\\x65',\n  '\\x2f', '\\x70', '\\x6e', '\\x67', '\\x3c', '\\x2f', '\\x64', '\\x63', '\\x3a', '\\x66',\n  '\\x6f', '\\x72', '\\x6d', '\\x61', '\\x74', '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x3c', '\\x2f', '\\x72', '\\x64', '\\x66', '\\x3a', '\\x44',\n  '\\x65', '\\x73', '\\x63', '\\x72', '\\x69', '\\x70', '\\x74', '\\x69', '\\x6f', '\\x6e',\n  '\\x3e', '\\x0a', '\\x20', '\\x20', '\\x20', '\\x3c', '\\x2f', '\\x72', '\\x64', '\\x66',\n  '\\x3a', '\\x52', '\\x44', '\\x46', '\\x3e', '\\x0a', '\\x3c', '\\x2f', '\\x78', '\\x3a',\n  '\\x78', '\\x6d', '\\x70', '\\x6d', '\\x65', '\\x74', '\\x61', '\\x3e', '\\x0a', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x0a',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x0a', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20', '\\x20',\n  '\\x20', '\\x20', '\\x35', '\\x1d', '\\x52', '\\x64', '\\x00', '\\x00', '\\x02', '\\x0b',\n  '\\x49', '\\x44', '\\x41', '\\x54', '\\x38', '\\x8d', '\\xa5', '\\x93', '\\x41', '\\x6b',\n  '\\x1a', '\\x41', '\\x18', '\\x86', '\\x9f', '\\x89', '\\x06', '\\x23', '\\x2e', '\\x74',\n  '\\xf5', '\\x20', '\\x4a', '\\x0e', '\\x6b', '\\x20', '\\xe4', '\\xe2', '\\xa5', '\\x78',\n  '\\xc8', '\\xa1', '\\x78', '\\x89', '\\x82', '\\x17', '\\x85', '\\x82', '\\x82', '\\x3d',\n  '\\x26', '\\x62', '\\x58', '\\x72', '\\xcd', '\\x4f', '\\xf1', '\\x18', '\\x4b', '\\x1b',\n  '\\x2f', '\\xa5', '\\x32', '\\xe0', '\\xb5', '\\x9e', '\\x35', '\\x1a', '\\x58', '\\x2f',\n  '\\xe9', '\\x0f', '\\xe8', '\\x65', '\\x6d', '\\x84', '\\xa2', '\\x84', '\\x2c', '\\x0a',\n  '\\x6b', '\\x58', '\\x62', '\\xa7', '\\x87', '\\x68', '\\x68', '\\x13', '\\xa1', '\\xa5',\n  '\\x79', '\\xe1', '\\x3b', '\\x0c', '\\xc3', '\\xfb', '\\xcc', '\\x37', '\\xef', '\\x7c',\n  '\\x23', '\\x94', '\\x52', '\\xbc', '\\x44', '\\x7e', '\\x00', '\\x21', '\\x04', '\\x00',\n  '\\xad', '\\x56', '\\xcb', '\\x18', '\\x8f', '\\xc7', '\\x0d', '\\xdb', '\\xb6', '\\xd3',\n  '\\x8e', '\\xe3', '\\xf8', '\\x83', '\\xc1', '\\xa0', '\\x8a', '\\xc7', '\\xe3', '\\x3f',\n  '\\x12', '\\x89', '\\x44', '\\xb9', '\\x5c', '\\x2e', '\\xf7', '\\x56', '\\xa6', '\\xdf',\n  '\\x0f', '\\x15', '\\x4a', '\\x29', '\\x84', '\\x10', '\\x34', '\\x9b', '\\xcd', '\\x77',\n  '\\x96', '\\x65', '\\x7d', '\\x36', '\\x0c', '\\x43', '\\xe4', '\\x72', '\\x39', '\\xe2',\n  '\\xf1', '\\x38', '\\xb7', '\\xb7', '\\xb7', '\\xf4', '\\xfb', '\\x7d', '\\x06', '\\x83',\n  '\\x01', '\\xa9', '\\x54', '\\xea', '\\x43', '\\xa5', '\\x52', '\\x39', '\\x7e', '\\x0a',\n  '\\x40', '\\x29', '\\x45', '\\xab', '\\xd5', '\\x32', '\\x4e', '\\x4f', '\\x4f', '\\x7f',\n  '\\x4a', '\\x29', '\\xd5', '\\xdd', '\\xdd', '\\xdd', '\\xb3', '\\xea', '\\xf5', '\\x7a',\n  '\\xea', '\\xe4', '\\xe4', '\\x44', '\\x49', '\\x29', '\\xd3', '\\x2b', '\\xcf', '\\xaa',\n  '\\x36', '\\x00', '\\xc6', '\\xe3', '\\x71', '\\x63', '\\x67', '\\x67', '\\x47', '\\xe4',\n  '\\xf3', '\\x79', '\\x00', '\\x3c', '\\xcf', '\\xa3', '\\x50', '\\x28', '\\xe0', '\\xba',\n  '\\x2e', '\\x9e', '\\xe7', '\\x91', '\\x4a', '\\xa5', '\\xd8', '\\xdb', '\\xdb', '\\x63',\n  '\\x34', '\\x1a', '\\x7d', '\\x7c', '\\x9a', '\\xc1', '\\x06', '\\x80', '\\x6d', '\\xdb',\n  '\\xe9', '\\x6c', '\\x36', '\\x8b', '\\xcf', '\\xe7', '\\xc3', '\\xf3', '\\x3c', '\\x5c',\n  '\\xd7', '\\x05', '\\x60', '\\x36', '\\x9b', '\\xe1', '\\xba', '\\x2e', '\\x8b', '\\xc5',\n  '\\x82', '\\x4c', '\\x26', '\\x83', '\\x6d', '\\xdb', '\\xbb', '\\x6b', '\\x43', '\\x74',\n  '\\x1c', '\\xc7', '\\x1f', '\\x8b', '\\xc5', '\\x00', '\\x28', '\\x16', '\\x8b', '\\x8f',\n  '\\x9b', '\\xd5', '\\x6a', '\\x15', '\\x00', '\\x29', '\\x25', '\\x9a', '\\xa6', '\\x31',\n  '\\x9f', '\\xcf', '\\xc5', '\\x5a', '\\xc0', '\\xd6', '\\xd6', '\\x96', '\\x9a', '\\x4c',\n  '\\x26', '\\x22', '\\x14', '\\x0a', '\\x21', '\\xa5', '\\x64', '\\x36', '\\x9b', '\\x51',\n  '\\xad', '\\x56', '\\xa9', '\\xd5', '\\x6a', '\\x68', '\\x9a', '\\x06', '\\xc0', '\\xf5',\n  '\\xf5', '\\x35', '\\xba', '\\xae', '\\xdf', '\\xaf', '\\xbd', '\\x82', '\\x61', '\\x18',\n  '\\xdf', '\\x2c', '\\xcb', '\\x7a', '\\x20', '\\xfa', '\\xfd', '\\x04', '\\x02', '\\x01',\n  '\\x00', '\\x34', '\\x4d', '\\x23', '\\x10', '\\x08', '\\xb0', '\\x58', '\\x2c', '\\xe8',\n  '\\x76', '\\xbb', '\\x24', '\\x12', '\\x89', '\\x8b', '\\xb5', '\\x1d', '\\x6c', '\\x6f',\n  '\\x6f', '\\x1f', '\\x75', '\\x3a', '\\x9d', '\\x7e', '\\x38', '\\x1c', '\\x26', '\\x97',\n  '\\xcb', '\\x21', '\\x84', '\\x40', '\\x4a', '\\x09', '\\xc0', '\\xe6', '\\xe6', '\\x26',\n  '\\xed', '\\x76', '\\x9b', '\\xc9', '\\x64', '\\xa2', '\\xa6', '\\xd3', '\\xe9', '\\x27',\n  '\\x78', '\\x98', '\\x9b', '\\xd5', '\\x53', '\\x3e', '\\xce', '\\xc1', '\\xf9', '\\xf9',\n  '\\xf9', '\\xfb', '\\xc1', '\\x60', '\\x70', '\\x9c', '\\x4c', '\\x26', '\\xd9', '\\xdf',\n  '\\xdf', '\\x27', '\\x12', '\\x89', '\\x30', '\\x1c', '\\x0e', '\\xb9', '\\xbc', '\\xbc',\n  '\\xe4', '\\xe6', '\\xe6', '\\x46', '\\xf9', '\\x7c', '\\xbe', '\\x2f', '\\xd3', '\\xe9',\n  '\\x34', '\\x0f', '\\x34', '\\xea', '\\xf5', '\\x7a', '\\xe5', '\\x19', '\\x60', '\\x19',\n  '\\xd6', '\\x9b', '\\xd1', '\\x68', '\\xd4', '\\x18', '\\x0e', '\\x87', '\\xbb', '\\xf3',\n  '\\xf9', '\\x5c', '\\xe8', '\\xba', '\\x7e', '\\x6f', '\\x18', '\\xc6', '\\x45', '\\x34',\n  '\\x1a', '\\x3d', '\\x2c', '\\x95', '\\x4a', '\\xdf', '\\x4d', '\\xd3', '\\xbc', '\\x02',\n  '\\x5e', '\\x03', '\\x5f', '\\x81', '\\x83', '\\xb3', '\\xb3', '\\x33', '\\xe7', '\\x0f',\n  '\\xc0', '\\xdf', '\\x64', '\\x9a', '\\xa6', '\\xb1', '\\x34', '\\xeb', '\\x80', '\\x03',\n  '\\x1c', '\\x6c', '\\xfc', '\\x93', '\\x73', '\\xa9', '\\x7a', '\\xbd', '\\x6e', '\\x03',\n  '\\x47', '\\xcb', '\\xa5', '\\x0e', '\\xbc', '\\xe2', '\\x7f', '\\x7e', '\\xa3', '\\x69',\n  '\\x9a', '\\x87', '\\xa6', '\\x69', '\\xbe', '\\x55', '\\x4a', '\\x3d', '\\x64', '\\xf0',\n  '\\x12', '\\xfd', '\\x02', '\\x0d', '\\x53', '\\x06', '\\x24', '\\x88', '\\x3f', '\\xe1',\n  '\\x69', '\\x00', '\\x00', '\\x00', '\\x00', '\\x49', '\\x45', '\\x4e', '\\x44', '\\xae',\n  '\\x42', '\\x60', '\\x82',\n};\n"
  },
  {
    "path": "muduo/net/inspect/Inspector.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_INSPECT_INSPECTOR_H\n#define MUDUO_NET_INSPECT_INSPECTOR_H\n\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/http/HttpRequest.h\"\n#include \"muduo/net/http/HttpServer.h\"\n\n#include <map>\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass ProcessInspector;\nclass PerformanceInspector;\nclass SystemInspector;\n\n// An internal inspector of the running process, usually a singleton.\n// Better to run in a seperated thread, as some method may block for seconds\nclass Inspector : noncopyable\n{\n public:\n  typedef std::vector<string> ArgList;\n  typedef std::function<string (HttpRequest::Method, const ArgList& args)> Callback;\n  Inspector(EventLoop* loop,\n            const InetAddress& httpAddr,\n            const string& name);\n  ~Inspector();\n\n  /// Add a Callback for handling the special uri : /mudule/command\n  void add(const string& module,\n           const string& command,\n           const Callback& cb,\n           const string& help);\n  void remove(const string& module, const string& command);\n\n private:\n  typedef std::map<string, Callback> CommandList;\n  typedef std::map<string, string> HelpList;\n\n  void start();\n  void onRequest(const HttpRequest& req, HttpResponse* resp);\n\n  HttpServer server_;\n  std::unique_ptr<ProcessInspector> processInspector_;\n  std::unique_ptr<PerformanceInspector> performanceInspector_;\n  std::unique_ptr<SystemInspector> systemInspector_;\n  MutexLock mutex_;\n  std::map<string, CommandList> modules_ GUARDED_BY(mutex_);\n  std::map<string, HelpList> helps_ GUARDED_BY(mutex_);\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_INSPECT_INSPECTOR_H\n"
  },
  {
    "path": "muduo/net/inspect/PerformanceInspector.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/inspect/PerformanceInspector.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/LogStream.h\"\n#include \"muduo/base/ProcessInfo.h\"\n\n#include <unistd.h>\n\n#ifdef HAVE_TCMALLOC\n#include <gperftools/malloc_extension.h>\n#include <gperftools/profiler.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid PerformanceInspector::registerCommands(Inspector* ins)\n{\n  ins->add(\"pprof\", \"heap\", PerformanceInspector::heap, \"get heap information\");\n  ins->add(\"pprof\", \"growth\", PerformanceInspector::growth, \"get heap growth information\");\n  ins->add(\"pprof\", \"profile\", PerformanceInspector::profile,\n           \"get cpu profiling information. CAUTION: blocking thread for 30 seconds!\");\n  ins->add(\"pprof\", \"cmdline\", PerformanceInspector::cmdline, \"get command line\");\n  ins->add(\"pprof\", \"memstats\", PerformanceInspector::memstats, \"get memory stats\");\n  ins->add(\"pprof\", \"memhistogram\", PerformanceInspector::memhistogram, \"get memory histogram\");\n  ins->add(\"pprof\", \"releasefreememory\", PerformanceInspector::releaseFreeMemory, \"release free memory\");\n}\n\nstring PerformanceInspector::heap(HttpRequest::Method, const Inspector::ArgList&)\n{\n  std::string result;\n  MallocExtension::instance()->GetHeapSample(&result);\n  return string(result.data(), result.size());\n}\n\nstring PerformanceInspector::growth(HttpRequest::Method, const Inspector::ArgList&)\n{\n  std::string result;\n  MallocExtension::instance()->GetHeapGrowthStacks(&result);\n  return string(result.data(), result.size());\n}\n\nstring PerformanceInspector::profile(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string filename = \"/tmp/\" + ProcessInfo::procname();\n  filename += \".\";\n  filename += ProcessInfo::pidString();\n  filename += \".\";\n  filename += Timestamp::now().toString();\n  filename += \".profile\";\n\n  string profile;\n  if (ProfilerStart(filename.c_str()))\n  {\n    // FIXME: async\n    CurrentThread::sleepUsec(30 * 1000 * 1000);\n    ProfilerStop();\n    FileUtil::readFile(filename, 1024*1024, &profile, NULL, NULL);\n    ::unlink(filename.c_str());\n  }\n  return profile;\n}\n\nstring PerformanceInspector::cmdline(HttpRequest::Method, const Inspector::ArgList&)\n{\n  return \"\";\n}\n\nstring PerformanceInspector::memstats(HttpRequest::Method, const Inspector::ArgList&)\n{\n  char buf[1024*64];\n  MallocExtension::instance()->GetStats(buf, sizeof buf);\n  return buf;\n}\n\nstring PerformanceInspector::memhistogram(HttpRequest::Method, const Inspector::ArgList&)\n{\n  int blocks = 0;\n  size_t total = 0;\n  int histogram[kMallocHistogramSize] = { 0, };\n\n  MallocExtension::instance()->MallocMemoryStats(&blocks, &total, histogram);\n  LogStream s;\n  s << \"blocks \" << blocks << \"\\ntotal \" << total << \"\\n\";\n  for (int i = 0; i < kMallocHistogramSize; ++i)\n    s << i << \" \" << histogram[i] << \"\\n\";\n  return s.buffer().toString();\n}\n\nstring PerformanceInspector::releaseFreeMemory(HttpRequest::Method, const Inspector::ArgList&)\n{\n  char buf[256];\n  snprintf(buf, sizeof buf, \"memory release rate: %f\\nAll free memory released.\\n\",\n           MallocExtension::instance()->GetMemoryReleaseRate());\n  MallocExtension::instance()->ReleaseFreeMemory();\n  return buf;\n}\n\n#endif\n"
  },
  {
    "path": "muduo/net/inspect/PerformanceInspector.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_INSPECT_PERFORMANCEINSPECTOR_H\n#define MUDUO_NET_INSPECT_PERFORMANCEINSPECTOR_H\n\n#include \"muduo/net/inspect/Inspector.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass PerformanceInspector : noncopyable\n{\n public:\n  void registerCommands(Inspector* ins);\n\n  static string heap(HttpRequest::Method, const Inspector::ArgList&);\n  static string growth(HttpRequest::Method, const Inspector::ArgList&);\n  static string profile(HttpRequest::Method, const Inspector::ArgList&);\n  static string cmdline(HttpRequest::Method, const Inspector::ArgList&);\n  static string memstats(HttpRequest::Method, const Inspector::ArgList&);\n  static string memhistogram(HttpRequest::Method, const Inspector::ArgList&);\n  static string releaseFreeMemory(HttpRequest::Method, const Inspector::ArgList&);\n\n  static string symbol(HttpRequest::Method, const Inspector::ArgList&);\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_INSPECT_PERFORMANCEINSPECTOR_H\n"
  },
  {
    "path": "muduo/net/inspect/ProcessInspector.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/inspect/ProcessInspector.h\"\n#include \"muduo/base/FileUtil.h\"\n#include \"muduo/base/ProcessInfo.h\"\n#include <limits.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace muduo\n{\nnamespace inspect\n{\n\nstring uptime(Timestamp now, Timestamp start, bool showMicroseconds)\n{\n  char buf[256];\n  int64_t age = now.microSecondsSinceEpoch() - start.microSecondsSinceEpoch();\n  int seconds = static_cast<int>(age / Timestamp::kMicroSecondsPerSecond);\n  int days = seconds/86400;\n  int hours = (seconds % 86400) / 3600;\n  int minutes = (seconds % 3600) / 60;\n  if (showMicroseconds)\n  {\n    int microseconds = static_cast<int>(age % Timestamp::kMicroSecondsPerSecond);\n    snprintf(buf, sizeof buf, \"%d days %02d:%02d:%02d.%06d\",\n             days, hours, minutes, seconds % 60, microseconds);\n  }\n  else\n  {\n    snprintf(buf, sizeof buf, \"%d days %02d:%02d:%02d\",\n             days, hours, minutes, seconds % 60);\n  }\n  return buf;\n}\n\nlong getLong(const string& procStatus, const char* key)\n{\n  long result = 0;\n  size_t pos = procStatus.find(key);\n  if (pos != string::npos)\n  {\n    result = ::atol(procStatus.c_str() + pos + strlen(key));\n  }\n  return result;\n}\n\nstring getProcessName(const string& procStatus)\n{\n  string result;\n  size_t pos = procStatus.find(\"Name:\");\n  if (pos != string::npos)\n  {\n    pos += strlen(\"Name:\");\n    while (procStatus[pos] == '\\t')\n      ++pos;\n    size_t eol = pos;\n    while (procStatus[eol] != '\\n')\n      ++eol;\n    result = procStatus.substr(pos, eol-pos);\n  }\n  return result;\n}\n\nStringPiece next(StringPiece data)\n{\n  const char* sp = static_cast<const char*>(::memchr(data.data(), ' ', data.size()));\n  if (sp)\n  {\n    data.remove_prefix(static_cast<int>(sp+1-data.begin()));\n    return data;\n  }\n  return \"\";\n}\n\nProcessInfo::CpuTime getCpuTime(StringPiece data)\n{\n  ProcessInfo::CpuTime t;\n\n  for (int i = 0; i < 10; ++i)\n  {\n    data = next(data);\n  }\n  long utime = strtol(data.data(), NULL, 10);\n  data = next(data);\n  long stime = strtol(data.data(), NULL, 10);\n  const double hz = static_cast<double>(ProcessInfo::clockTicksPerSecond());\n  t.userSeconds = static_cast<double>(utime) / hz;\n  t.systemSeconds = static_cast<double>(stime) / hz;\n  return t;\n}\n\nint stringPrintf(string* out, const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));\n\nint stringPrintf(string* out, const char* fmt, ...)\n{\n  char buf[256];\n  va_list args;\n  va_start(args, fmt);\n  int ret = vsnprintf(buf, sizeof buf, fmt, args);\n  va_end(args);\n  out->append(buf);\n  return ret;\n}\n\n}  // namespace inspect\n}  // namespace muduo\n\nusing namespace muduo::inspect;\n\nstring ProcessInspector::username_ = ProcessInfo::username();\n\nvoid ProcessInspector::registerCommands(Inspector* ins)\n{\n  ins->add(\"proc\", \"overview\", ProcessInspector::overview, \"print basic overview\");\n  ins->add(\"proc\", \"pid\", ProcessInspector::pid, \"print pid\");\n  ins->add(\"proc\", \"status\", ProcessInspector::procStatus, \"print /proc/self/status\");\n  // ins->add(\"proc\", \"opened_files\", ProcessInspector::openedFiles, \"count /proc/self/fd\");\n  ins->add(\"proc\", \"threads\", ProcessInspector::threads, \"list /proc/self/task\");\n}\n\nstring ProcessInspector::overview(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string result;\n  result.reserve(1024);\n  Timestamp now = Timestamp::now();\n  result += \"Page generated at \";\n  result += now.toFormattedString();\n  result += \" (UTC)\\nStarted at \";\n  result += ProcessInfo::startTime().toFormattedString();\n  result += \" (UTC), up for \";\n  result += uptime(now, ProcessInfo::startTime(), true/* show microseconds */);\n  result += \"\\n\";\n\n  string procStatus = ProcessInfo::procStatus();\n  result += getProcessName(procStatus);\n  result += \" (\";\n  result += ProcessInfo::exePath();\n  result += \") running as \";\n  result += username_;\n  result += \" on \";\n  result += ProcessInfo::hostname(); // cache ?\n  result += \"\\n\";\n\n  if (ProcessInfo::isDebugBuild())\n  {\n    result += \"WARNING: debug build!\\n\";\n  }\n\n  stringPrintf(&result, \"pid %d, num of threads %ld, bits %zd\\n\",\n               ProcessInfo::pid(), getLong(procStatus, \"Threads:\"), CHAR_BIT * sizeof(void*));\n\n  result += \"Virtual memory: \";\n  stringPrintf(&result, \"%.3f MiB, \",\n               static_cast<double>(getLong(procStatus, \"VmSize:\")) / 1024.0);\n\n  result += \"RSS memory: \";\n  stringPrintf(&result, \"%.3f MiB\\n\",\n               static_cast<double>(getLong(procStatus, \"VmRSS:\")) / 1024.0);\n\n  // FIXME: VmData:\n\n  stringPrintf(&result, \"Opened files: %d, limit: %d\\n\",\n               ProcessInfo::openedFiles(), ProcessInfo::maxOpenFiles());\n\n  // string procStat = ProcessInfo::procStat();\n\n  /*\n  stringPrintf(&result, \"ppid %ld\\n\", getStatField(procStat, 0));\n  stringPrintf(&result, \"pgid %ld\\n\", getStatField(procStat, 1));\n  */\n\n  ProcessInfo::CpuTime t = ProcessInfo::cpuTime();\n  stringPrintf(&result, \"User time: %12.3fs\\nSys time:  %12.3fs\\n\",\n               t.userSeconds, t.systemSeconds);\n\n  // FIXME: add context switches\n\n  return result;\n}\n\nstring ProcessInspector::pid(HttpRequest::Method, const Inspector::ArgList&)\n{\n  char buf[32];\n  snprintf(buf, sizeof buf, \"%d\", ProcessInfo::pid());\n  return buf;\n}\n\nstring ProcessInspector::procStatus(HttpRequest::Method, const Inspector::ArgList&)\n{\n  return ProcessInfo::procStatus();\n}\n\nstring ProcessInspector::openedFiles(HttpRequest::Method, const Inspector::ArgList&)\n{\n  char buf[32];\n  snprintf(buf, sizeof buf, \"%d\", ProcessInfo::openedFiles());\n  return buf;\n}\n\nstring ProcessInspector::threads(HttpRequest::Method, const Inspector::ArgList&)\n{\n  std::vector<pid_t> threads = ProcessInfo::threads();\n  string result = \"  TID NAME             S    User Time  System Time\\n\";\n  result.reserve(threads.size() * 64);\n  string stat;\n  for (pid_t tid : threads)\n  {\n    char buf[256];\n    snprintf(buf, sizeof buf, \"/proc/%d/task/%d/stat\", ProcessInfo::pid(), tid);\n    if (FileUtil::readFile(buf, 65536, &stat) == 0)\n    {\n      StringPiece name = ProcessInfo::procname(stat);\n      const char* rp = name.end();\n      assert(*rp == ')');\n      const char* state = rp + 2;\n      *const_cast<char*>(rp) = '\\0';  // don't do this at home\n      StringPiece data(stat);\n      data.remove_prefix(static_cast<int>(state - data.data() + 2));\n      ProcessInfo::CpuTime t = getCpuTime(data);\n      snprintf(buf, sizeof buf, \"%5d %-16s %c %12.3f %12.3f\\n\",\n               tid, name.data(), *state, t.userSeconds, t.systemSeconds);\n      result += buf;\n    }\n  }\n  return result;\n}\n\n"
  },
  {
    "path": "muduo/net/inspect/ProcessInspector.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_INSPECT_PROCESSINSPECTOR_H\n#define MUDUO_NET_INSPECT_PROCESSINSPECTOR_H\n\n#include \"muduo/net/inspect/Inspector.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass ProcessInspector : noncopyable\n{\n public:\n  void registerCommands(Inspector* ins);\n\n  static string overview(HttpRequest::Method, const Inspector::ArgList&);\n  static string pid(HttpRequest::Method, const Inspector::ArgList&);\n  static string procStatus(HttpRequest::Method, const Inspector::ArgList&);\n  static string openedFiles(HttpRequest::Method, const Inspector::ArgList&);\n  static string threads(HttpRequest::Method, const Inspector::ArgList&);\n\n  static string username_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_INSPECT_PROCESSINSPECTOR_H\n"
  },
  {
    "path": "muduo/net/inspect/SystemInspector.cc",
    "content": "// Copyright 2014, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n\n#include \"muduo/net/inspect/SystemInspector.h\"\n#include \"muduo/base/FileUtil.h\"\n\n#include <sys/utsname.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace muduo\n{\nnamespace inspect\n{\nstring uptime(Timestamp now, Timestamp start, bool showMicroseconds);\nlong getLong(const string& content, const char* key);\nint stringPrintf(string* out, const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));\n}\n}\n\nusing namespace muduo::inspect;\n\nvoid SystemInspector::registerCommands(Inspector* ins)\n{\n  ins->add(\"sys\", \"overview\", SystemInspector::overview, \"print system overview\");\n  ins->add(\"sys\", \"loadavg\", SystemInspector::loadavg, \"print /proc/loadavg\");\n  ins->add(\"sys\", \"version\", SystemInspector::version, \"print /proc/version\");\n  ins->add(\"sys\", \"cpuinfo\", SystemInspector::cpuinfo, \"print /proc/cpuinfo\");\n  ins->add(\"sys\", \"meminfo\", SystemInspector::meminfo, \"print /proc/meminfo\");\n  ins->add(\"sys\", \"stat\", SystemInspector::stat, \"print /proc/stat\");\n}\n\nstring SystemInspector::loadavg(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string loadavg;\n  FileUtil::readFile(\"/proc/loadavg\", 65536, &loadavg);\n  return loadavg;\n}\n\nstring SystemInspector::version(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string version;\n  FileUtil::readFile(\"/proc/version\", 65536, &version);\n  return version;\n}\n\nstring SystemInspector::cpuinfo(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string cpuinfo;\n  FileUtil::readFile(\"/proc/cpuinfo\", 65536, &cpuinfo);\n  return cpuinfo;\n}\n\nstring SystemInspector::meminfo(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string meminfo;\n  FileUtil::readFile(\"/proc/meminfo\", 65536, &meminfo);\n  return meminfo;\n}\n\nstring SystemInspector::stat(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string stat;\n  FileUtil::readFile(\"/proc/stat\", 65536, &stat);\n  return stat;\n}\n\nstring SystemInspector::overview(HttpRequest::Method, const Inspector::ArgList&)\n{\n  string result;\n  result.reserve(1024);\n  Timestamp now = Timestamp::now();\n  result += \"Page generated at \";\n  result += now.toFormattedString();\n  result += \" (UTC)\\n\";\n  // Hardware and OS\n  {\n  struct utsname un;\n  if (::uname(&un) == 0)\n  {\n    stringPrintf(&result, \"Hostname: %s\\n\", un.nodename);\n    stringPrintf(&result, \"Machine: %s\\n\", un.machine);\n    stringPrintf(&result, \"OS: %s %s %s\\n\", un.sysname, un.release, un.version);\n  }\n  }\n  string stat;\n  FileUtil::readFile(\"/proc/stat\", 65536, &stat);\n  Timestamp bootTime(Timestamp::kMicroSecondsPerSecond * getLong(stat, \"btime \"));\n  result += \"Boot time: \";\n  result += bootTime.toFormattedString(false /* show microseconds */);\n  result += \" (UTC)\\n\";\n  result += \"Up time: \";\n  result += uptime(now, bootTime, false /* show microseconds */);\n  result += \"\\n\";\n\n  // CPU load\n  {\n  string loadavg;\n  FileUtil::readFile(\"/proc/loadavg\", 65536, &loadavg);\n  stringPrintf(&result, \"Processes created: %ld\\n\", getLong(stat, \"processes \"));\n  stringPrintf(&result, \"Loadavg: %s\\n\", loadavg.c_str());\n  }\n\n  // Memory\n  {\n  string meminfo;\n  FileUtil::readFile(\"/proc/meminfo\", 65536, &meminfo);\n  long total_kb = getLong(meminfo, \"MemTotal:\");\n  long free_kb = getLong(meminfo, \"MemFree:\");\n  long buffers_kb = getLong(meminfo, \"Buffers:\");\n  long cached_kb = getLong(meminfo, \"Cached:\");\n\n  stringPrintf(&result, \"Total Memory: %6ld MiB\\n\", total_kb / 1024);\n  stringPrintf(&result, \"Free Memory:  %6ld MiB\\n\", free_kb / 1024);\n  stringPrintf(&result, \"Buffers:      %6ld MiB\\n\", buffers_kb / 1024);\n  stringPrintf(&result, \"Cached:       %6ld MiB\\n\", cached_kb / 1024);\n  stringPrintf(&result, \"Real Used:    %6ld MiB\\n\", (total_kb - free_kb - buffers_kb - cached_kb) / 1024);\n  stringPrintf(&result, \"Real Free:    %6ld MiB\\n\", (free_kb + buffers_kb + cached_kb) / 1024);\n\n  // Swap\n  }\n  // Disk\n  // Network\n  return result;\n}\n"
  },
  {
    "path": "muduo/net/inspect/SystemInspector.h",
    "content": "// Copyright 2014, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_INSPECT_SYSTEMINSPECTOR_H\n#define MUDUO_NET_INSPECT_SYSTEMINSPECTOR_H\n\n#include \"muduo/net/inspect/Inspector.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass SystemInspector : noncopyable\n{\n public:\n  void registerCommands(Inspector* ins);\n\n  static string overview(HttpRequest::Method, const Inspector::ArgList&);\n  static string loadavg(HttpRequest::Method, const Inspector::ArgList&);\n  static string version(HttpRequest::Method, const Inspector::ArgList&);\n  static string cpuinfo(HttpRequest::Method, const Inspector::ArgList&);\n  static string meminfo(HttpRequest::Method, const Inspector::ArgList&);\n  static string stat(HttpRequest::Method, const Inspector::ArgList&);\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_INSPECT_SYSTEMINSPECTOR_H\n"
  },
  {
    "path": "muduo/net/inspect/tests/BUILD.bazel",
    "content": "cc_binary(\n    name = \"inspector\",\n    srcs = [\"Inspector_test.cc\"],\n    deps = [\n        \"//muduo/net/inspect\",\n    ],\n)\n"
  },
  {
    "path": "muduo/net/inspect/tests/Inspector_test.cc",
    "content": "#include \"muduo/net/inspect/Inspector.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main()\n{\n  EventLoop loop;\n  EventLoopThread t;\n  Inspector ins(t.startLoop(), InetAddress(12345), \"test\");\n  loop.loop();\n}\n\n"
  },
  {
    "path": "muduo/net/poller/DefaultPoller.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/Poller.h\"\n#include \"muduo/net/poller/PollPoller.h\"\n#include \"muduo/net/poller/EPollPoller.h\"\n\n#include <stdlib.h>\n\nusing namespace muduo::net;\n\nPoller* Poller::newDefaultPoller(EventLoop* loop)\n{\n  if (::getenv(\"MUDUO_USE_POLL\"))\n  {\n    return new PollPoller(loop);\n  }\n  else\n  {\n    return new EPollPoller(loop);\n  }\n}\n"
  },
  {
    "path": "muduo/net/poller/EPollPoller.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/poller/EPollPoller.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <poll.h>\n#include <sys/epoll.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\n// On Linux, the constants of poll(2) and epoll(4)\n// are expected to be the same.\nstatic_assert(EPOLLIN == POLLIN,        \"epoll uses same flag values as poll\");\nstatic_assert(EPOLLPRI == POLLPRI,      \"epoll uses same flag values as poll\");\nstatic_assert(EPOLLOUT == POLLOUT,      \"epoll uses same flag values as poll\");\nstatic_assert(EPOLLRDHUP == POLLRDHUP,  \"epoll uses same flag values as poll\");\nstatic_assert(EPOLLERR == POLLERR,      \"epoll uses same flag values as poll\");\nstatic_assert(EPOLLHUP == POLLHUP,      \"epoll uses same flag values as poll\");\n\nnamespace\n{\nconst int kNew = -1;\nconst int kAdded = 1;\nconst int kDeleted = 2;\n}\n\nEPollPoller::EPollPoller(EventLoop* loop)\n  : Poller(loop),\n    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),\n    events_(kInitEventListSize)\n{\n  if (epollfd_ < 0)\n  {\n    LOG_SYSFATAL << \"EPollPoller::EPollPoller\";\n  }\n}\n\nEPollPoller::~EPollPoller()\n{\n  ::close(epollfd_);\n}\n\nTimestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)\n{\n  LOG_TRACE << \"fd total count \" << channels_.size();\n  int numEvents = ::epoll_wait(epollfd_,\n                               &*events_.begin(),\n                               static_cast<int>(events_.size()),\n                               timeoutMs);\n  int savedErrno = errno;\n  Timestamp now(Timestamp::now());\n  if (numEvents > 0)\n  {\n    LOG_TRACE << numEvents << \" events happened\";\n    fillActiveChannels(numEvents, activeChannels);\n    if (implicit_cast<size_t>(numEvents) == events_.size())\n    {\n      events_.resize(events_.size()*2);\n    }\n  }\n  else if (numEvents == 0)\n  {\n    LOG_TRACE << \"nothing happened\";\n  }\n  else\n  {\n    // error happens, log uncommon ones\n    if (savedErrno != EINTR)\n    {\n      errno = savedErrno;\n      LOG_SYSERR << \"EPollPoller::poll()\";\n    }\n  }\n  return now;\n}\n\nvoid EPollPoller::fillActiveChannels(int numEvents,\n                                     ChannelList* activeChannels) const\n{\n  assert(implicit_cast<size_t>(numEvents) <= events_.size());\n  for (int i = 0; i < numEvents; ++i)\n  {\n    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);\n#ifndef NDEBUG\n    int fd = channel->fd();\n    ChannelMap::const_iterator it = channels_.find(fd);\n    assert(it != channels_.end());\n    assert(it->second == channel);\n#endif\n    channel->set_revents(events_[i].events);\n    activeChannels->push_back(channel);\n  }\n}\n\nvoid EPollPoller::updateChannel(Channel* channel)\n{\n  Poller::assertInLoopThread();\n  const int index = channel->index();\n  LOG_TRACE << \"fd = \" << channel->fd()\n    << \" events = \" << channel->events() << \" index = \" << index;\n  if (index == kNew || index == kDeleted)\n  {\n    // a new one, add with EPOLL_CTL_ADD\n    int fd = channel->fd();\n    if (index == kNew)\n    {\n      assert(channels_.find(fd) == channels_.end());\n      channels_[fd] = channel;\n    }\n    else // index == kDeleted\n    {\n      assert(channels_.find(fd) != channels_.end());\n      assert(channels_[fd] == channel);\n    }\n\n    channel->set_index(kAdded);\n    update(EPOLL_CTL_ADD, channel);\n  }\n  else\n  {\n    // update existing one with EPOLL_CTL_MOD/DEL\n    int fd = channel->fd();\n    (void)fd;\n    assert(channels_.find(fd) != channels_.end());\n    assert(channels_[fd] == channel);\n    assert(index == kAdded);\n    if (channel->isNoneEvent())\n    {\n      update(EPOLL_CTL_DEL, channel);\n      channel->set_index(kDeleted);\n    }\n    else\n    {\n      update(EPOLL_CTL_MOD, channel);\n    }\n  }\n}\n\nvoid EPollPoller::removeChannel(Channel* channel)\n{\n  Poller::assertInLoopThread();\n  int fd = channel->fd();\n  LOG_TRACE << \"fd = \" << fd;\n  assert(channels_.find(fd) != channels_.end());\n  assert(channels_[fd] == channel);\n  assert(channel->isNoneEvent());\n  int index = channel->index();\n  assert(index == kAdded || index == kDeleted);\n  size_t n = channels_.erase(fd);\n  (void)n;\n  assert(n == 1);\n\n  if (index == kAdded)\n  {\n    update(EPOLL_CTL_DEL, channel);\n  }\n  channel->set_index(kNew);\n}\n\nvoid EPollPoller::update(int operation, Channel* channel)\n{\n  struct epoll_event event;\n  memZero(&event, sizeof event);\n  event.events = channel->events();\n  event.data.ptr = channel;\n  int fd = channel->fd();\n  LOG_TRACE << \"epoll_ctl op = \" << operationToString(operation)\n    << \" fd = \" << fd << \" event = { \" << channel->eventsToString() << \" }\";\n  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)\n  {\n    if (operation == EPOLL_CTL_DEL)\n    {\n      LOG_SYSERR << \"epoll_ctl op =\" << operationToString(operation) << \" fd =\" << fd;\n    }\n    else\n    {\n      LOG_SYSFATAL << \"epoll_ctl op =\" << operationToString(operation) << \" fd =\" << fd;\n    }\n  }\n}\n\nconst char* EPollPoller::operationToString(int op)\n{\n  switch (op)\n  {\n    case EPOLL_CTL_ADD:\n      return \"ADD\";\n    case EPOLL_CTL_DEL:\n      return \"DEL\";\n    case EPOLL_CTL_MOD:\n      return \"MOD\";\n    default:\n      assert(false && \"ERROR op\");\n      return \"Unknown Operation\";\n  }\n}\n"
  },
  {
    "path": "muduo/net/poller/EPollPoller.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H\n#define MUDUO_NET_POLLER_EPOLLPOLLER_H\n\n#include \"muduo/net/Poller.h\"\n\n#include <vector>\n\nstruct epoll_event;\n\nnamespace muduo\n{\nnamespace net\n{\n\n///\n/// IO Multiplexing with epoll(4).\n///\nclass EPollPoller : public Poller\n{\n public:\n  EPollPoller(EventLoop* loop);\n  ~EPollPoller() override;\n\n  Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;\n  void updateChannel(Channel* channel) override;\n  void removeChannel(Channel* channel) override;\n\n private:\n  static const int kInitEventListSize = 16;\n\n  static const char* operationToString(int op);\n\n  void fillActiveChannels(int numEvents,\n                          ChannelList* activeChannels) const;\n  void update(int operation, Channel* channel);\n\n  typedef std::vector<struct epoll_event> EventList;\n\n  int epollfd_;\n  EventList events_;\n};\n\n}  // namespace net\n}  // namespace muduo\n#endif  // MUDUO_NET_POLLER_EPOLLPOLLER_H\n"
  },
  {
    "path": "muduo/net/poller/PollPoller.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/poller/PollPoller.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Types.h\"\n#include \"muduo/net/Channel.h\"\n\n#include <assert.h>\n#include <errno.h>\n#include <poll.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nPollPoller::PollPoller(EventLoop* loop)\n  : Poller(loop)\n{\n}\n\nPollPoller::~PollPoller() = default;\n\nTimestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)\n{\n  // XXX pollfds_ shouldn't change\n  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);\n  int savedErrno = errno;\n  Timestamp now(Timestamp::now());\n  if (numEvents > 0)\n  {\n    LOG_TRACE << numEvents << \" events happened\";\n    fillActiveChannels(numEvents, activeChannels);\n  }\n  else if (numEvents == 0)\n  {\n    LOG_TRACE << \" nothing happened\";\n  }\n  else\n  {\n    if (savedErrno != EINTR)\n    {\n      errno = savedErrno;\n      LOG_SYSERR << \"PollPoller::poll()\";\n    }\n  }\n  return now;\n}\n\nvoid PollPoller::fillActiveChannels(int numEvents,\n                                    ChannelList* activeChannels) const\n{\n  for (PollFdList::const_iterator pfd = pollfds_.begin();\n      pfd != pollfds_.end() && numEvents > 0; ++pfd)\n  {\n    if (pfd->revents > 0)\n    {\n      --numEvents;\n      ChannelMap::const_iterator ch = channels_.find(pfd->fd);\n      assert(ch != channels_.end());\n      Channel* channel = ch->second;\n      assert(channel->fd() == pfd->fd);\n      channel->set_revents(pfd->revents);\n      // pfd->revents = 0;\n      activeChannels->push_back(channel);\n    }\n  }\n}\n\nvoid PollPoller::updateChannel(Channel* channel)\n{\n  Poller::assertInLoopThread();\n  LOG_TRACE << \"fd = \" << channel->fd() << \" events = \" << channel->events();\n  if (channel->index() < 0)\n  {\n    // a new one, add to pollfds_\n    assert(channels_.find(channel->fd()) == channels_.end());\n    struct pollfd pfd;\n    pfd.fd = channel->fd();\n    pfd.events = static_cast<short>(channel->events());\n    pfd.revents = 0;\n    pollfds_.push_back(pfd);\n    int idx = static_cast<int>(pollfds_.size())-1;\n    channel->set_index(idx);\n    channels_[pfd.fd] = channel;\n  }\n  else\n  {\n    // update existing one\n    assert(channels_.find(channel->fd()) != channels_.end());\n    assert(channels_[channel->fd()] == channel);\n    int idx = channel->index();\n    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));\n    struct pollfd& pfd = pollfds_[idx];\n    assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);\n    pfd.fd = channel->fd();\n    pfd.events = static_cast<short>(channel->events());\n    pfd.revents = 0;\n    if (channel->isNoneEvent())\n    {\n      // ignore this pollfd\n      pfd.fd = -channel->fd()-1;\n    }\n  }\n}\n\nvoid PollPoller::removeChannel(Channel* channel)\n{\n  Poller::assertInLoopThread();\n  LOG_TRACE << \"fd = \" << channel->fd();\n  assert(channels_.find(channel->fd()) != channels_.end());\n  assert(channels_[channel->fd()] == channel);\n  assert(channel->isNoneEvent());\n  int idx = channel->index();\n  assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));\n  const struct pollfd& pfd = pollfds_[idx]; (void)pfd;\n  assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());\n  size_t n = channels_.erase(channel->fd());\n  assert(n == 1); (void)n;\n  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)\n  {\n    pollfds_.pop_back();\n  }\n  else\n  {\n    int channelAtEnd = pollfds_.back().fd;\n    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);\n    if (channelAtEnd < 0)\n    {\n      channelAtEnd = -channelAtEnd-1;\n    }\n    channels_[channelAtEnd]->set_index(idx);\n    pollfds_.pop_back();\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/poller/PollPoller.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is an internal header file, you should not include this.\n\n#ifndef MUDUO_NET_POLLER_POLLPOLLER_H\n#define MUDUO_NET_POLLER_POLLPOLLER_H\n\n#include \"muduo/net/Poller.h\"\n\n#include <vector>\n\nstruct pollfd;\n\nnamespace muduo\n{\nnamespace net\n{\n\n///\n/// IO Multiplexing with poll(2).\n///\nclass PollPoller : public Poller\n{\n public:\n\n  PollPoller(EventLoop* loop);\n  ~PollPoller() override;\n\n  Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;\n  void updateChannel(Channel* channel) override;\n  void removeChannel(Channel* channel) override;\n\n private:\n  void fillActiveChannels(int numEvents,\n                          ChannelList* activeChannels) const;\n\n  typedef std::vector<struct pollfd> PollFdList;\n  PollFdList pollfds_;\n};\n\n}  // namespace net\n}  // namespace muduo\n#endif  // MUDUO_NET_POLLER_POLLPOLLER_H\n"
  },
  {
    "path": "muduo/net/protobuf/BufferStream.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n#pragma once\n#include \"muduo/net/Buffer.h\"\n#include <google/protobuf/io/zero_copy_stream.h>\nnamespace muduo\n{\nnamespace net\n{\n\n// FIXME:\n// class BufferInputStream : google::protobuf::io::ZeroCopyInputStream\n// {\n// };\n\nclass BufferOutputStream : public google::protobuf::io::ZeroCopyOutputStream\n{\n public:\n  BufferOutputStream(Buffer* buf)\n    : buffer_(CHECK_NOTNULL(buf)),\n      originalSize_(buffer_->readableBytes())\n  {\n  }\n\n  virtual bool Next(void** data, int* size) // override\n  {\n    buffer_->ensureWritableBytes(4096);\n    *data = buffer_->beginWrite();\n    *size = static_cast<int>(buffer_->writableBytes());\n    buffer_->hasWritten(*size);\n    return true;\n  }\n\n  virtual void BackUp(int count) // override\n  {\n    buffer_->unwrite(count);\n  }\n\n  virtual int64_t ByteCount() const // override\n  {\n    return buffer_->readableBytes() - originalSize_;\n  }\n\n private:\n  Buffer* buffer_;\n  size_t originalSize_;\n};\n\n}\n}\n"
  },
  {
    "path": "muduo/net/protobuf/CMakeLists.txt",
    "content": "add_library(muduo_protobuf_codec ProtobufCodecLite.cc)\nset_target_properties(muduo_protobuf_codec PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(muduo_protobuf_codec muduo_net protobuf z)\n\n#add_library(muduo_protobuf_codec_cpp11 ProtobufCodecLite.cc)\n#set_target_properties(muduo_protobuf_codec_cpp11 PROPERTIES COMPILE_FLAGS \"-std=c++0x -Wno-error=shadow\")\n#target_link_libraries(muduo_protobuf_codec_cpp11 muduo_net_cpp11 protobuf z)\n\n\ninstall(TARGETS muduo_protobuf_codec DESTINATION lib)\n#install(TARGETS muduo_protobuf_codec_cpp11 DESTINATION lib)\n\nfile(GLOB HEADERS \"*.h\")\ninstall(FILES ${HEADERS} DESTINATION include/muduo/net/protobuf)\n\n"
  },
  {
    "path": "muduo/net/protobuf/ProtobufCodecLite.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/protobuf/ProtobufCodecLite.h\"\n// #include <muduo/net/protobuf/BufferStream.h>\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/TcpConnection.h\"\n#include \"muduo/net/protorpc/google-inl.h\"\n\n#include <google/protobuf/message.h>\n#include <zlib.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace\n{\n  int ProtobufVersionCheck()\n  {\n    GOOGLE_PROTOBUF_VERIFY_VERSION;\n    return 0;\n  }\n  int __attribute__ ((unused)) dummy = ProtobufVersionCheck();\n}\n\nvoid ProtobufCodecLite::send(const TcpConnectionPtr& conn,\n                             const ::google::protobuf::Message& message)\n{\n  // FIXME: serialize to TcpConnection::outputBuffer()\n  muduo::net::Buffer buf;\n  fillEmptyBuffer(&buf, message);\n  conn->send(&buf);\n}\n\nvoid ProtobufCodecLite::fillEmptyBuffer(muduo::net::Buffer* buf,\n                                        const google::protobuf::Message& message)\n{\n  assert(buf->readableBytes() == 0);\n  // FIXME: can we move serialization & checksum to other thread?\n  buf->append(tag_);\n\n  int byte_size = serializeToBuffer(message, buf);\n\n  int32_t checkSum = checksum(buf->peek(), static_cast<int>(buf->readableBytes()));\n  buf->appendInt32(checkSum);\n  assert(buf->readableBytes() == tag_.size() + byte_size + kChecksumLen); (void) byte_size;\n  int32_t len = sockets::hostToNetwork32(static_cast<int32_t>(buf->readableBytes()));\n  buf->prepend(&len, sizeof len);\n}\n\nvoid ProtobufCodecLite::onMessage(const TcpConnectionPtr& conn,\n                                  Buffer* buf,\n                                  Timestamp receiveTime)\n{\n  while (buf->readableBytes() >= static_cast<uint32_t>(kMinMessageLen+kHeaderLen))\n  {\n    const int32_t len = buf->peekInt32();\n    if (len > kMaxMessageLen || len < kMinMessageLen)\n    {\n      errorCallback_(conn, buf, receiveTime, kInvalidLength);\n      break;\n    }\n    else if (buf->readableBytes() >= implicit_cast<size_t>(kHeaderLen+len))\n    {\n      if (rawCb_ && !rawCb_(conn, StringPiece(buf->peek(), kHeaderLen+len), receiveTime))\n      {\n        buf->retrieve(kHeaderLen+len);\n        continue;\n      }\n      MessagePtr message(prototype_->New());\n      // FIXME: can we move deserialization & callback to other thread?\n      ErrorCode errorCode = parse(buf->peek()+kHeaderLen, len, message.get());\n      if (errorCode == kNoError)\n      {\n        // FIXME: try { } catch (...) { }\n        messageCallback_(conn, message, receiveTime);\n        buf->retrieve(kHeaderLen+len);\n      }\n      else\n      {\n        errorCallback_(conn, buf, receiveTime, errorCode);\n        break;\n      }\n    }\n    else\n    {\n      break;\n    }\n  }\n}\n\nbool ProtobufCodecLite::parseFromBuffer(StringPiece buf, google::protobuf::Message* message)\n{\n  return message->ParseFromArray(buf.data(), buf.size());\n}\n\nint ProtobufCodecLite::serializeToBuffer(const google::protobuf::Message& message, Buffer* buf)\n{\n  // TODO: use BufferOutputStream\n  // BufferOutputStream os(buf);\n  // message.SerializeToZeroCopyStream(&os);\n  // return static_cast<int>(os.ByteCount());\n\n  // code copied from MessageLite::SerializeToArray() and MessageLite::SerializePartialToArray().\n  GOOGLE_DCHECK(message.IsInitialized()) << InitializationErrorMessage(\"serialize\", message);\n\n  /**\n   * 'ByteSize()' of message is deprecated in Protocol Buffers v3.4.0 firstly. But, till to v3.11.0, it just getting start to be marked by '__attribute__((deprecated()))'.\n   * So, here, v3.9.2 is selected as maximum version using 'ByteSize()' to avoid potential effect for previous muduo code/projects as far as possible.\n   * Note: All information above just INFER from \n   * 1) https://github.com/protocolbuffers/protobuf/releases/tag/v3.4.0\n   * 2) MACRO in file 'include/google/protobuf/port_def.inc'. eg. '#define PROTOBUF_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))'.\n   * In addition, usage of 'ToIntSize()' comes from Impl of ByteSize() in new version's Protocol Buffers.\n   */\n\n  #if GOOGLE_PROTOBUF_VERSION > 3009002\n    int byte_size = google::protobuf::internal::ToIntSize(message.ByteSizeLong());\n  #else\n    int byte_size = message.ByteSize();\n  #endif\n  buf->ensureWritableBytes(byte_size + kChecksumLen);\n\n  uint8_t* start = reinterpret_cast<uint8_t*>(buf->beginWrite());\n  uint8_t* end = message.SerializeWithCachedSizesToArray(start);\n  if (end - start != byte_size)\n  {\n    #if GOOGLE_PROTOBUF_VERSION > 3009002\n      ByteSizeConsistencyError(byte_size, google::protobuf::internal::ToIntSize(message.ByteSizeLong()), static_cast<int>(end - start));\n    #else\n      ByteSizeConsistencyError(byte_size, message.ByteSize(), static_cast<int>(end - start));\n    #endif\n  }\n  buf->hasWritten(byte_size);\n  return byte_size;\n}\n\nnamespace\n{\n  const string kNoErrorStr = \"NoError\";\n  const string kInvalidLengthStr = \"InvalidLength\";\n  const string kCheckSumErrorStr = \"CheckSumError\";\n  const string kInvalidNameLenStr = \"InvalidNameLen\";\n  const string kUnknownMessageTypeStr = \"UnknownMessageType\";\n  const string kParseErrorStr = \"ParseError\";\n  const string kUnknownErrorStr = \"UnknownError\";\n}\n\nconst string& ProtobufCodecLite::errorCodeToString(ErrorCode errorCode)\n{\n  switch (errorCode)\n  {\n   case kNoError:\n     return kNoErrorStr;\n   case kInvalidLength:\n     return kInvalidLengthStr;\n   case kCheckSumError:\n     return kCheckSumErrorStr;\n   case kInvalidNameLen:\n     return kInvalidNameLenStr;\n   case kUnknownMessageType:\n     return kUnknownMessageTypeStr;\n   case kParseError:\n     return kParseErrorStr;\n   default:\n     return kUnknownErrorStr;\n  }\n}\n\nvoid ProtobufCodecLite::defaultErrorCallback(const TcpConnectionPtr& conn,\n                                             Buffer* buf,\n                                             Timestamp,\n                                             ErrorCode errorCode)\n{\n  LOG_ERROR << \"ProtobufCodecLite::defaultErrorCallback - \" << errorCodeToString(errorCode);\n  if (conn && conn->connected())\n  {\n    conn->shutdown();\n  }\n}\n\nint32_t ProtobufCodecLite::asInt32(const char* buf)\n{\n  int32_t be32 = 0;\n  ::memcpy(&be32, buf, sizeof(be32));\n  return sockets::networkToHost32(be32);\n}\n\nint32_t ProtobufCodecLite::checksum(const void* buf, int len)\n{\n  return static_cast<int32_t>(\n      ::adler32(1, static_cast<const Bytef*>(buf), len));\n}\n\nbool ProtobufCodecLite::validateChecksum(const char* buf, int len)\n{\n  // check sum\n  int32_t expectedCheckSum = asInt32(buf + len - kChecksumLen);\n  int32_t checkSum = checksum(buf, len - kChecksumLen);\n  return checkSum == expectedCheckSum;\n}\n\nProtobufCodecLite::ErrorCode ProtobufCodecLite::parse(const char* buf,\n                                                      int len,\n                                                      ::google::protobuf::Message* message)\n{\n  ErrorCode error = kNoError;\n\n  if (validateChecksum(buf, len))\n  {\n    if (memcmp(buf, tag_.data(), tag_.size()) == 0)\n    {\n      // parse from buffer\n      const char* data = buf + tag_.size();\n      int32_t dataLen = len - kChecksumLen - static_cast<int>(tag_.size());\n      if (parseFromBuffer(StringPiece(data, dataLen), message))\n      {\n        error = kNoError;\n      }\n      else\n      {\n        error = kParseError;\n      }\n    }\n    else\n    {\n      error = kUnknownMessageType;\n    }\n  }\n  else\n  {\n    error = kCheckSumError;\n  }\n\n  return error;\n}\n\n"
  },
  {
    "path": "muduo/net/protobuf/ProtobufCodecLite.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n// For Protobuf codec supporting multiple message types, check\n// examples/protobuf/codec\n\n#ifndef MUDUO_NET_PROTOBUF_PROTOBUFCODECLITE_H\n#define MUDUO_NET_PROTOBUF_PROTOBUFCODECLITE_H\n\n#include \"muduo/base/noncopyable.h\"\n#include \"muduo/base/StringPiece.h\"\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/Callbacks.h\"\n\n#include <memory>\n#include <type_traits>\n\nnamespace google\n{\nnamespace protobuf\n{\nclass Message;\n}\n}\n\nnamespace muduo\n{\nnamespace net\n{\n\ntypedef std::shared_ptr<google::protobuf::Message> MessagePtr;\n\n// wire format\n//\n// Field     Length  Content\n//\n// size      4-byte  M+N+4\n// tag       M-byte  could be \"RPC0\", etc.\n// payload   N-byte\n// checksum  4-byte  adler32 of tag+payload\n//\n// This is an internal class, you should use ProtobufCodecT instead.\nclass ProtobufCodecLite : noncopyable\n{\n public:\n  const static int kHeaderLen = sizeof(int32_t);\n  const static int kChecksumLen = sizeof(int32_t);\n  const static int kMaxMessageLen = 64*1024*1024; // same as codec_stream.h kDefaultTotalBytesLimit\n\n  enum ErrorCode\n  {\n    kNoError = 0,\n    kInvalidLength,\n    kCheckSumError,\n    kInvalidNameLen,\n    kUnknownMessageType,\n    kParseError,\n  };\n\n  // return false to stop parsing protobuf message\n  typedef std::function<bool (const TcpConnectionPtr&,\n                              StringPiece,\n                              Timestamp)> RawMessageCallback;\n\n  typedef std::function<void (const TcpConnectionPtr&,\n                              const MessagePtr&,\n                              Timestamp)> ProtobufMessageCallback;\n\n  typedef std::function<void (const TcpConnectionPtr&,\n                              Buffer*,\n                              Timestamp,\n                              ErrorCode)> ErrorCallback;\n\n  ProtobufCodecLite(const ::google::protobuf::Message* prototype,\n                    StringPiece tagArg,\n                    const ProtobufMessageCallback& messageCb,\n                    const RawMessageCallback& rawCb = RawMessageCallback(),\n                    const ErrorCallback& errorCb = defaultErrorCallback)\n    : prototype_(prototype),\n      tag_(tagArg.as_string()),\n      messageCallback_(messageCb),\n      rawCb_(rawCb),\n      errorCallback_(errorCb),\n      kMinMessageLen(tagArg.size() + kChecksumLen)\n  {\n  }\n\n  virtual ~ProtobufCodecLite() = default;\n\n  const string& tag() const { return tag_; }\n\n  void send(const TcpConnectionPtr& conn,\n            const ::google::protobuf::Message& message);\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp receiveTime);\n\n  virtual bool parseFromBuffer(StringPiece buf, google::protobuf::Message* message);\n  virtual int serializeToBuffer(const google::protobuf::Message& message, Buffer* buf);\n\n  static const string& errorCodeToString(ErrorCode errorCode);\n\n  // public for unit tests\n  ErrorCode parse(const char* buf, int len, ::google::protobuf::Message* message);\n  void fillEmptyBuffer(muduo::net::Buffer* buf, const google::protobuf::Message& message);\n\n  static int32_t checksum(const void* buf, int len);\n  static bool validateChecksum(const char* buf, int len);\n  static int32_t asInt32(const char* buf);\n  static void defaultErrorCallback(const TcpConnectionPtr&,\n                                   Buffer*,\n                                   Timestamp,\n                                   ErrorCode);\n\n private:\n  const ::google::protobuf::Message* prototype_;\n  const string tag_;\n  ProtobufMessageCallback messageCallback_;\n  RawMessageCallback rawCb_;\n  ErrorCallback errorCallback_;\n  const int kMinMessageLen;\n};\n\ntemplate<typename MSG, const char* TAG, typename CODEC=ProtobufCodecLite>  // TAG must be a variable with external linkage, not a string literal\nclass ProtobufCodecLiteT\n{\n  static_assert(std::is_base_of<ProtobufCodecLite, CODEC>::value, \"CODEC should be derived from ProtobufCodecLite\");\n public:\n  typedef std::shared_ptr<MSG> ConcreteMessagePtr;\n  typedef std::function<void (const TcpConnectionPtr&,\n                              const ConcreteMessagePtr&,\n                              Timestamp)> ProtobufMessageCallback;\n  typedef ProtobufCodecLite::RawMessageCallback RawMessageCallback;\n  typedef ProtobufCodecLite::ErrorCallback ErrorCallback;\n\n  explicit ProtobufCodecLiteT(const ProtobufMessageCallback& messageCb,\n                              const RawMessageCallback& rawCb = RawMessageCallback(),\n                              const ErrorCallback& errorCb = ProtobufCodecLite::defaultErrorCallback)\n    : messageCallback_(messageCb),\n      codec_(&MSG::default_instance(),\n             TAG,\n             std::bind(&ProtobufCodecLiteT::onRpcMessage, this, _1, _2, _3),\n             rawCb,\n             errorCb)\n  {\n  }\n\n  const string& tag() const { return codec_.tag(); }\n\n  void send(const TcpConnectionPtr& conn,\n            const MSG& message)\n  {\n    codec_.send(conn, message);\n  }\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp receiveTime)\n  {\n    codec_.onMessage(conn, buf, receiveTime);\n  }\n\n  // internal\n  void onRpcMessage(const TcpConnectionPtr& conn,\n                    const MessagePtr& message,\n                    Timestamp receiveTime)\n  {\n    messageCallback_(conn, ::muduo::down_pointer_cast<MSG>(message), receiveTime);\n  }\n\n  void fillEmptyBuffer(muduo::net::Buffer* buf, const MSG& message)\n  {\n    codec_.fillEmptyBuffer(buf, message);\n  }\n\n private:\n  ProtobufMessageCallback messageCallback_;\n  CODEC codec_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_PROTOBUF_PROTOBUFCODECLITE_H\n"
  },
  {
    "path": "muduo/net/protorpc/CMakeLists.txt",
    "content": "add_custom_command(OUTPUT rpc.pb.cc rpc.pb.h\n  COMMAND protoc\n  ARGS --cpp_out . ${CMAKE_CURRENT_SOURCE_DIR}/rpc.proto -I${CMAKE_CURRENT_SOURCE_DIR}\n  DEPENDS rpc.proto\n  VERBATIM )\n\nset_source_files_properties(rpc.pb.cc PROPERTIES COMPILE_FLAGS \"-Wno-conversion\")\ninclude_directories(${PROJECT_BINARY_DIR})\n\nadd_library(muduo_protorpc_wire rpc.pb.cc RpcCodec.cc)\nset_target_properties(muduo_protorpc_wire PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\n\n#add_library(muduo_protorpc_wire_cpp11 rpc.pb.cc RpcCodec.cc)\n#set_target_properties(muduo_protorpc_wire_cpp11 PROPERTIES COMPILE_FLAGS \"-std=c++0x -Wno-error=shadow\")\n\nif(MUDUO_BUILD_EXAMPLES)\nadd_executable(protobuf_rpc_wire_test RpcCodec_test.cc)\ntarget_link_libraries(protobuf_rpc_wire_test muduo_protorpc_wire muduo_protobuf_codec)\nset_target_properties(protobuf_rpc_wire_test PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\nendif()\n\nadd_library(muduo_protorpc RpcChannel.cc RpcServer.cc)\nset_target_properties(muduo_protorpc PROPERTIES COMPILE_FLAGS \"-Wno-error=shadow\")\ntarget_link_libraries(muduo_protorpc muduo_protorpc_wire muduo_protobuf_codec muduo_net protobuf z)\n\nif(TCMALLOC_LIBRARY)\n  target_link_libraries(muduo_protorpc tcmalloc_and_profiler)\nendif()\n\ninstall(TARGETS muduo_protorpc_wire muduo_protorpc DESTINATION lib)\n#install(TARGETS muduo_protorpc_wire_cpp11 DESTINATION lib)\n\nset(HEADERS\n  RpcCodec.h\n  RpcChannel.h\n  RpcServer.h\n  rpc.proto\n  rpcservice.proto\n  ${PROJECT_BINARY_DIR}/muduo/net/protorpc/rpc.pb.h\n  )\ninstall(FILES ${HEADERS} DESTINATION include/muduo/net/protorpc)\n\n"
  },
  {
    "path": "muduo/net/protorpc/README",
    "content": "This is an proof of concept implementation of Google Protobuf RPC using muduo.\nThe object lifetime management is for from ideal and doesn't follow the usual\nmuduo approach.\n\nPlease consider using http://github.com/chenshuo/muduo-protorpc instead.\n"
  },
  {
    "path": "muduo/net/protorpc/RpcChannel.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/protorpc/RpcChannel.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/protorpc/rpc.pb.h\"\n\n#include <google/protobuf/descriptor.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nRpcChannel::RpcChannel()\n  : codec_(std::bind(&RpcChannel::onRpcMessage, this, _1, _2, _3)),\n    services_(NULL)\n{\n  LOG_INFO << \"RpcChannel::ctor - \" << this;\n}\n\nRpcChannel::RpcChannel(const TcpConnectionPtr& conn)\n  : codec_(std::bind(&RpcChannel::onRpcMessage, this, _1, _2, _3)),\n    conn_(conn),\n    services_(NULL)\n{\n  LOG_INFO << \"RpcChannel::ctor - \" << this;\n}\n\nRpcChannel::~RpcChannel()\n{\n  LOG_INFO << \"RpcChannel::dtor - \" << this;\n  for (const auto& outstanding : outstandings_)\n  {\n    OutstandingCall out = outstanding.second;\n    delete out.response;\n    delete out.done;\n  }\n}\n\n  // Call the given method of the remote service.  The signature of this\n  // procedure looks the same as Service::CallMethod(), but the requirements\n  // are less strict in one important way:  the request and response objects\n  // need not be of any specific class as long as their descriptors are\n  // method->input_type() and method->output_type().\nvoid RpcChannel::CallMethod(const ::google::protobuf::MethodDescriptor* method,\n                            google::protobuf::RpcController* controller,\n                            const ::google::protobuf::Message* request,\n                            ::google::protobuf::Message* response,\n                            ::google::protobuf::Closure* done)\n{\n  RpcMessage message;\n  message.set_type(REQUEST);\n  int64_t id = id_.incrementAndGet();\n  message.set_id(id);\n  message.set_service(method->service()->full_name());\n  message.set_method(method->name());\n  message.set_request(request->SerializeAsString()); // FIXME: error check\n\n  OutstandingCall out = { response, done };\n  {\n  MutexLockGuard lock(mutex_);\n  outstandings_[id] = out;\n  }\n  codec_.send(conn_, message);\n}\n\nvoid RpcChannel::onMessage(const TcpConnectionPtr& conn,\n                           Buffer* buf,\n                           Timestamp receiveTime)\n{\n  codec_.onMessage(conn, buf, receiveTime);\n}\n\nvoid RpcChannel::onRpcMessage(const TcpConnectionPtr& conn,\n                              const RpcMessagePtr& messagePtr,\n                              Timestamp receiveTime)\n{\n  assert(conn == conn_);\n  //printf(\"%s\\n\", message.DebugString().c_str());\n  RpcMessage& message = *messagePtr;\n  if (message.type() == RESPONSE)\n  {\n    int64_t id = message.id();\n    assert(message.has_response() || message.has_error());\n\n    OutstandingCall out = { NULL, NULL };\n\n    {\n      MutexLockGuard lock(mutex_);\n      std::map<int64_t, OutstandingCall>::iterator it = outstandings_.find(id);\n      if (it != outstandings_.end())\n      {\n        out = it->second;\n        outstandings_.erase(it);\n      }\n    }\n\n    if (out.response)\n    {\n      std::unique_ptr<google::protobuf::Message> d(out.response);\n      if (message.has_response())\n      {\n        out.response->ParseFromString(message.response());\n      }\n      if (out.done)\n      {\n        out.done->Run();\n      }\n    }\n  }\n  else if (message.type() == REQUEST)\n  {\n    // FIXME: extract to a function\n    ErrorCode error = WRONG_PROTO;\n    if (services_)\n    {\n      std::map<std::string, google::protobuf::Service*>::const_iterator it = services_->find(message.service());\n      if (it != services_->end())\n      {\n        google::protobuf::Service* service = it->second;\n        assert(service != NULL);\n        const google::protobuf::ServiceDescriptor* desc = service->GetDescriptor();\n        const google::protobuf::MethodDescriptor* method\n          = desc->FindMethodByName(message.method());\n        if (method)\n        {\n          std::unique_ptr<google::protobuf::Message> request(service->GetRequestPrototype(method).New());\n          if (request->ParseFromString(message.request()))\n          {\n            google::protobuf::Message* response = service->GetResponsePrototype(method).New();\n            // response is deleted in doneCallback\n            int64_t id = message.id();\n            service->CallMethod(method, NULL, get_pointer(request), response,\n                                NewCallback(this, &RpcChannel::doneCallback, response, id));\n            error = NO_ERROR;\n          }\n          else\n          {\n            error = INVALID_REQUEST;\n          }\n        }\n        else\n        {\n          error = NO_METHOD;\n        }\n      }\n      else\n      {\n        error = NO_SERVICE;\n      }\n    }\n    else\n    {\n      error = NO_SERVICE;\n    }\n    if (error != NO_ERROR)\n    {\n      RpcMessage response;\n      response.set_type(RESPONSE);\n      response.set_id(message.id());\n      response.set_error(error);\n      codec_.send(conn_, response);\n    }\n  }\n  else if (message.type() == ERROR)\n  {\n  }\n}\n\nvoid RpcChannel::doneCallback(::google::protobuf::Message* response, int64_t id)\n{\n  std::unique_ptr<google::protobuf::Message> d(response);\n  RpcMessage message;\n  message.set_type(RESPONSE);\n  message.set_id(id);\n  message.set_response(response->SerializeAsString()); // FIXME: error check\n  codec_.send(conn_, message);\n}\n\n"
  },
  {
    "path": "muduo/net/protorpc/RpcChannel.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_PROTORPC_RPCCHANNEL_H\n#define MUDUO_NET_PROTORPC_RPCCHANNEL_H\n\n#include \"muduo/base/Atomic.h\"\n#include \"muduo/base/Mutex.h\"\n#include \"muduo/net/protorpc/RpcCodec.h\"\n\n#include <google/protobuf/service.h>\n\n#include <map>\n\n// Service and RpcChannel classes are incorporated from\n// google/protobuf/service.h\n\n// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc.  All rights reserved.\n// http://code.google.com/p/protobuf/\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//     * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Author: kenton@google.com (Kenton Varda)\n//  Based on original Protocol Buffers design by\n//  Sanjay Ghemawat, Jeff Dean, and others.\n\nnamespace google {\nnamespace protobuf {\n\n// Defined in other files.\nclass Descriptor;            // descriptor.h\nclass ServiceDescriptor;     // descriptor.h\nclass MethodDescriptor;      // descriptor.h\nclass Message;               // message.h\n\nclass Closure;\n\nclass RpcController;\nclass Service;\n\n}  // namespace protobuf\n}  // namespace google\n\n\nnamespace muduo\n{\nnamespace net\n{\n\n// Abstract interface for an RPC channel.  An RpcChannel represents a\n// communication line to a Service which can be used to call that Service's\n// methods.  The Service may be running on another machine.  Normally, you\n// should not call an RpcChannel directly, but instead construct a stub Service\n// wrapping it.  Example:\n// FIXME: update here\n//   RpcChannel* channel = new MyRpcChannel(\"remotehost.example.com:1234\");\n//   MyService* service = new MyService::Stub(channel);\n//   service->MyMethod(request, &response, callback);\nclass RpcChannel : public ::google::protobuf::RpcChannel\n{\n public:\n  RpcChannel();\n\n  explicit RpcChannel(const TcpConnectionPtr& conn);\n\n  ~RpcChannel() override;\n\n  void setConnection(const TcpConnectionPtr& conn)\n  {\n    conn_ = conn;\n  }\n\n  void setServices(const std::map<std::string, ::google::protobuf::Service*>* services)\n  {\n    services_ = services;\n  }\n\n  // Call the given method of the remote service.  The signature of this\n  // procedure looks the same as Service::CallMethod(), but the requirements\n  // are less strict in one important way:  the request and response objects\n  // need not be of any specific class as long as their descriptors are\n  // method->input_type() and method->output_type().\n  void CallMethod(const ::google::protobuf::MethodDescriptor* method,\n                  ::google::protobuf::RpcController* controller,\n                  const ::google::protobuf::Message* request,\n                  ::google::protobuf::Message* response,\n                  ::google::protobuf::Closure* done) override;\n\n  void onMessage(const TcpConnectionPtr& conn,\n                 Buffer* buf,\n                 Timestamp receiveTime);\n\n private:\n  void onRpcMessage(const TcpConnectionPtr& conn,\n                    const RpcMessagePtr& messagePtr,\n                    Timestamp receiveTime);\n\n  void doneCallback(::google::protobuf::Message* response, int64_t id);\n\n  struct OutstandingCall\n  {\n    ::google::protobuf::Message* response;\n    ::google::protobuf::Closure* done;\n  };\n\n  RpcCodec codec_;\n  TcpConnectionPtr conn_;\n  AtomicInt64 id_;\n\n  MutexLock mutex_;\n  std::map<int64_t, OutstandingCall> outstandings_ GUARDED_BY(mutex_);\n\n  const std::map<std::string, ::google::protobuf::Service*>* services_;\n};\ntypedef std::shared_ptr<RpcChannel> RpcChannelPtr;\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_PROTORPC_RPCCHANNEL_H\n"
  },
  {
    "path": "muduo/net/protorpc/RpcCodec.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/protorpc/RpcCodec.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Endian.h\"\n#include \"muduo/net/TcpConnection.h\"\n\n#include \"muduo/net/protorpc/rpc.pb.h\"\n#include \"muduo/net/protorpc/google-inl.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nnamespace\n{\n  int ProtobufVersionCheck()\n  {\n    GOOGLE_PROTOBUF_VERIFY_VERSION;\n    return 0;\n  }\n  int dummy __attribute__ ((unused)) = ProtobufVersionCheck();\n}\n\nnamespace muduo\n{\nnamespace net\n{\nconst char rpctag [] = \"RPC0\";\n}\n}\n"
  },
  {
    "path": "muduo/net/protorpc/RpcCodec.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_PROTORPC_RPCCODEC_H\n#define MUDUO_NET_PROTORPC_RPCCODEC_H\n\n#include \"muduo/base/Timestamp.h\"\n#include \"muduo/net/protobuf/ProtobufCodecLite.h\"\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass Buffer;\nclass TcpConnection;\ntypedef std::shared_ptr<TcpConnection> TcpConnectionPtr;\n\nclass RpcMessage;\ntypedef std::shared_ptr<RpcMessage> RpcMessagePtr;\nextern const char rpctag[];// = \"RPC0\";\n\n// wire format\n//\n// Field     Length  Content\n//\n// size      4-byte  N+8\n// \"RPC0\"    4-byte\n// payload   N-byte\n// checksum  4-byte  adler32 of \"RPC0\"+payload\n//\n\ntypedef ProtobufCodecLiteT<RpcMessage, rpctag> RpcCodec;\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_PROTORPC_RPCCODEC_H\n"
  },
  {
    "path": "muduo/net/protorpc/RpcCodec_test.cc",
    "content": "#undef NDEBUG\n#include \"muduo/net/protorpc/RpcCodec.h\"\n#include \"muduo/net/protorpc/rpc.pb.h\"\n#include \"muduo/net/protobuf/ProtobufCodecLite.h\"\n#include \"muduo/net/Buffer.h\"\n\n#include <stdio.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid rpcMessageCallback(const TcpConnectionPtr&,\n                        const RpcMessagePtr&,\n                        Timestamp)\n{\n}\n\nMessagePtr g_msgptr;\nvoid messageCallback(const TcpConnectionPtr&,\n                     const MessagePtr& msg,\n                     Timestamp)\n{\n  g_msgptr = msg;\n}\n\nvoid print(const Buffer& buf)\n{\n  printf(\"encoded to %zd bytes\\n\", buf.readableBytes());\n  for (size_t i = 0; i < buf.readableBytes(); ++i)\n  {\n    unsigned char ch = static_cast<unsigned char>(buf.peek()[i]);\n\n    printf(\"%2zd:  0x%02x  %c\\n\", i, ch, isgraph(ch) ? ch : ' ');\n  }\n}\n\nchar rpctag[] = \"RPC0\";\n\nint main()\n{\n  RpcMessage message;\n  message.set_type(REQUEST);\n  message.set_id(2);\n  char wire[] = \"\\0\\0\\0\\x13\" \"RPC0\" \"\\x08\\x01\\x11\\x02\\0\\0\\0\\0\\0\\0\\0\" \"\\x0f\\xef\\x01\\x32\";\n  string expected(wire, sizeof(wire)-1);\n  string s1, s2;\n  Buffer buf1, buf2;\n  {\n  RpcCodec codec(rpcMessageCallback);\n  codec.fillEmptyBuffer(&buf1, message);\n  print(buf1);\n  s1 = buf1.toStringPiece().as_string();\n  }\n\n  {\n  ProtobufCodecLite codec(&RpcMessage::default_instance(), \"RPC0\", messageCallback);\n  codec.fillEmptyBuffer(&buf2, message);\n  print(buf2);\n  s2 = buf2.toStringPiece().as_string();\n  codec.onMessage(TcpConnectionPtr(), &buf1, Timestamp::now());\n  assert(g_msgptr);\n  assert(g_msgptr->DebugString() == message.DebugString());\n  g_msgptr.reset();\n  }\n  assert(s1 == s2);\n  assert(s1 == expected);\n  assert(s2 == expected);\n\n  {\n  Buffer buf;\n  ProtobufCodecLite codec(&RpcMessage::default_instance(), \"XYZ\", messageCallback);\n  codec.fillEmptyBuffer(&buf, message);\n  print(buf);\n  s2 = buf.toStringPiece().as_string();\n  codec.onMessage(TcpConnectionPtr(), &buf, Timestamp::now());\n  assert(g_msgptr);\n  assert(g_msgptr->DebugString() == message.DebugString());\n  }\n\n  google::protobuf::ShutdownProtobufLibrary();\n}\n"
  },
  {
    "path": "muduo/net/protorpc/RpcServer.cc",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n\n#include \"muduo/net/protorpc/RpcServer.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/protorpc/RpcChannel.h\"\n\n#include <google/protobuf/descriptor.h>\n#include <google/protobuf/service.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nRpcServer::RpcServer(EventLoop* loop,\n                     const InetAddress& listenAddr)\n  : server_(loop, listenAddr, \"RpcServer\")\n{\n  server_.setConnectionCallback(\n      std::bind(&RpcServer::onConnection, this, _1));\n//   server_.setMessageCallback(\n//       std::bind(&RpcServer::onMessage, this, _1, _2, _3));\n}\n\nvoid RpcServer::registerService(google::protobuf::Service* service)\n{\n  const google::protobuf::ServiceDescriptor* desc = service->GetDescriptor();\n  services_[desc->full_name()] = service;\n}\n\nvoid RpcServer::start()\n{\n  server_.start();\n}\n\nvoid RpcServer::onConnection(const TcpConnectionPtr& conn)\n{\n  LOG_INFO << \"RpcServer - \" << conn->peerAddress().toIpPort() << \" -> \"\n    << conn->localAddress().toIpPort() << \" is \"\n    << (conn->connected() ? \"UP\" : \"DOWN\");\n  if (conn->connected())\n  {\n    RpcChannelPtr channel(new RpcChannel(conn));\n    channel->setServices(&services_);\n    conn->setMessageCallback(\n        std::bind(&RpcChannel::onMessage, get_pointer(channel), _1, _2, _3));\n    conn->setContext(channel);\n  }\n  else\n  {\n    conn->setContext(RpcChannelPtr());\n    // FIXME:\n  }\n}\n\n// void RpcServer::onMessage(const TcpConnectionPtr& conn,\n//                           Buffer* buf,\n//                           Timestamp time)\n// {\n//   RpcChannelPtr& channel = boost::any_cast<RpcChannelPtr&>(conn->getContext());\n//   channel->onMessage(conn, buf, time);\n// }\n\n"
  },
  {
    "path": "muduo/net/protorpc/RpcServer.h",
    "content": "// Copyright 2010, Shuo Chen.  All rights reserved.\n// http://code.google.com/p/muduo/\n//\n// Use of this source code is governed by a BSD-style license\n// that can be found in the License file.\n\n// Author: Shuo Chen (chenshuo at chenshuo dot com)\n//\n// This is a public header file, it must only include public header files.\n\n#ifndef MUDUO_NET_PROTORPC_RPCSERVER_H\n#define MUDUO_NET_PROTORPC_RPCSERVER_H\n\n#include \"muduo/net/TcpServer.h\"\n\nnamespace google {\nnamespace protobuf {\n\nclass Service;\n\n}  // namespace protobuf\n}  // namespace google\n\nnamespace muduo\n{\nnamespace net\n{\n\nclass RpcServer\n{\n public:\n  RpcServer(EventLoop* loop,\n            const InetAddress& listenAddr);\n\n  void setThreadNum(int numThreads)\n  {\n    server_.setThreadNum(numThreads);\n  }\n\n  void registerService(::google::protobuf::Service*);\n  void start();\n\n private:\n  void onConnection(const TcpConnectionPtr& conn);\n\n  // void onMessage(const TcpConnectionPtr& conn,\n  //                Buffer* buf,\n  //                Timestamp time);\n\n  TcpServer server_;\n  std::map<std::string, ::google::protobuf::Service*> services_;\n};\n\n}  // namespace net\n}  // namespace muduo\n\n#endif  // MUDUO_NET_PROTORPC_RPCSERVER_H\n"
  },
  {
    "path": "muduo/net/protorpc/google-inl.h",
    "content": "// ByteSizeConsistencyError and InitializationErrorMessage are\n// copied from google/protobuf/message_lite.cc\n\n// Protocol Buffers - Google's data interchange format\n// Copyright 2008 Google Inc.  All rights reserved.\n// http://code.google.com/p/protobuf/\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//     * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Authors: wink@google.com (Wink Saville),\n//          kenton@google.com (Kenton Varda)\n//  Based on original Protocol Buffers design by\n//  Sanjay Ghemawat, Jeff Dean, and others.\n\n#include <google/protobuf/message.h>\n\n// When serializing, we first compute the byte size, then serialize the message.\n// If serialization produces a different number of bytes than expected, we\n// call this function, which crashes.  The problem could be due to a bug in the\n// protobuf implementation but is more likely caused by concurrent modification\n// of the message.  This function attempts to distinguish between the two and\n// provide a useful error message.\ninline\nvoid ByteSizeConsistencyError(int byte_size_before_serialization,\n                              int byte_size_after_serialization,\n                              int bytes_produced_by_serialization)\n{\n  GOOGLE_CHECK_EQ(byte_size_before_serialization, byte_size_after_serialization)\n      << \"Protocol message was modified concurrently during serialization.\";\n  GOOGLE_CHECK_EQ(bytes_produced_by_serialization, byte_size_before_serialization)\n      << \"Byte size calculation and serialization were inconsistent.  This \"\n         \"may indicate a bug in protocol buffers or it may be caused by \"\n         \"concurrent modification of the message.\";\n  GOOGLE_LOG(FATAL) << \"This shouldn't be called if all the sizes are equal.\";\n}\n\ninline\nstd::string InitializationErrorMessage(const char* action,\n                                       const google::protobuf::MessageLite& message)\n{\n  // Note:  We want to avoid depending on strutil in the lite library, otherwise\n  //   we'd use:\n  //\n  // return strings::Substitute(\n  //   \"Can't $0 message of type \\\"$1\\\" because it is missing required \"\n  //   \"fields: $2\",\n  //   action, message.GetTypeName(),\n  //   message.InitializationErrorString());\n\n  std::string result;\n  result += \"Can't \";\n  result += action;\n  result += \" message of type \\\"\";\n  result += message.GetTypeName();\n  result += \"\\\" because it is missing required fields: \";\n  result += message.InitializationErrorString();\n  return result;\n}\n\n\n"
  },
  {
    "path": "muduo/net/protorpc/rpc.proto",
    "content": "package muduo.net;\n// option go_package = \"muduorpc\";\noption java_package = \"com.chenshuo.muduo.protorpc\";\noption java_outer_classname = \"RpcProto\";\n\nenum MessageType\n{\n  REQUEST = 1;\n  RESPONSE = 2;\n  ERROR = 3; // not used\n}\n\nenum ErrorCode\n{\n  NO_ERROR = 0;\n  WRONG_PROTO = 1;\n  NO_SERVICE = 2;\n  NO_METHOD = 3;\n  INVALID_REQUEST = 4;\n  INVALID_RESPONSE = 5;\n  TIMEOUT = 6;\n}\n\nmessage RpcMessage\n{\n  required MessageType type = 1;\n  required fixed64 id = 2;\n\n  optional string service = 3;\n  optional string method = 4;\n  optional bytes request = 5;\n\n  optional bytes response = 6;\n\n  optional ErrorCode error = 7;\n}\n"
  },
  {
    "path": "muduo/net/protorpc/rpcservice.proto",
    "content": "package muduo.net;\n\noption cc_generic_services = true;\noption java_generic_services = true;\noption py_generic_services = true;\n\noption java_package = \"com.chenshuo.muduo.protorpc\";\noption java_outer_classname = \"RpcServiceProto\";\n\n//import \"google/protobuf/descriptor.proto\";\nimport \"rpc.proto\";\n\nmessage ListRpcRequest\n{\n  optional string service_name = 1;\n  optional bool list_method = 2;\n}\n\nmessage ListRpcResponse\n{\n  required ErrorCode error = 1;\n  repeated string service_name = 2;\n  repeated string method_name = 3;\n}\n\nmessage GetServiceRequest\n{\n  required string service_name = 1;\n}\n\nmessage GetServiceResponse\n{\n  required ErrorCode error = 1;\n  repeated string proto_file = 2;\n  repeated string proto_file_name = 3;\n}\n\n// the meta service\nservice RpcService\n{\n  rpc listRpc (ListRpcRequest) returns (ListRpcResponse);\n  rpc getService (GetServiceRequest) returns (GetServiceResponse);\n}\n\n"
  },
  {
    "path": "muduo/net/tests/Buffer_unittest.cc",
    "content": "#include \"muduo/net/Buffer.h\"\n\n//#define BOOST_TEST_MODULE BufferTest\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\nusing muduo::string;\nusing muduo::net::Buffer;\n\nBOOST_AUTO_TEST_CASE(testBufferAppendRetrieve)\n{\n  Buffer buf;\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n\n  const string str(200, 'x');\n  buf.append(str);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), str.size());\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize - str.size());\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n\n  const string str2 =  buf.retrieveAsString(50);\n  BOOST_CHECK_EQUAL(str2.size(), 50);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), str.size() - str2.size());\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize - str.size());\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend + str2.size());\n  BOOST_CHECK_EQUAL(str2, string(50, 'x'));\n\n  buf.append(str);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 2*str.size() - str2.size());\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize - 2*str.size());\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend + str2.size());\n\n  const string str3 =  buf.retrieveAllAsString();\n  BOOST_CHECK_EQUAL(str3.size(), 350);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n  BOOST_CHECK_EQUAL(str3, string(350, 'x'));\n}\n\nBOOST_AUTO_TEST_CASE(testBufferGrow)\n{\n  Buffer buf;\n  buf.append(string(400, 'y'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 400);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-400);\n\n  buf.retrieve(50);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 350);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-400);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend+50);\n\n  buf.append(string(1000, 'z'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 1350);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend+50); // FIXME\n\n  buf.retrieveAll();\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), 1400); // FIXME\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n}\n\nBOOST_AUTO_TEST_CASE(testBufferInsideGrow)\n{\n  Buffer buf;\n  buf.append(string(800, 'y'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 800);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-800);\n\n  buf.retrieve(500);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 300);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-800);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend+500);\n\n  buf.append(string(300, 'z'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 600);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-600);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n}\n\nBOOST_AUTO_TEST_CASE(testBufferShrink)\n{\n  Buffer buf;\n  buf.append(string(2000, 'y'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 2000);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n\n  buf.retrieve(1500);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 500);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend+1500);\n\n  buf.shrink(0);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 500);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-500);\n  BOOST_CHECK_EQUAL(buf.retrieveAllAsString(), string(500, 'y'));\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n}\n\nBOOST_AUTO_TEST_CASE(testBufferPrepend)\n{\n  Buffer buf;\n  buf.append(string(200, 'y'));\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 200);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-200);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend);\n\n  int x = 0;\n  buf.prepend(&x, sizeof x);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 204);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize-200);\n  BOOST_CHECK_EQUAL(buf.prependableBytes(), Buffer::kCheapPrepend - 4);\n}\n\nBOOST_AUTO_TEST_CASE(testBufferReadInt)\n{\n  Buffer buf;\n  buf.append(\"HTTP\");\n\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 4);\n  BOOST_CHECK_EQUAL(buf.peekInt8(), 'H');\n  int top16 = buf.peekInt16();\n  BOOST_CHECK_EQUAL(top16, 'H'*256 + 'T');\n  BOOST_CHECK_EQUAL(buf.peekInt32(), top16*65536 + 'T'*256 + 'P');\n\n  BOOST_CHECK_EQUAL(buf.readInt8(), 'H');\n  BOOST_CHECK_EQUAL(buf.readInt16(), 'T'*256 + 'T');\n  BOOST_CHECK_EQUAL(buf.readInt8(), 'P');\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 0);\n  BOOST_CHECK_EQUAL(buf.writableBytes(), Buffer::kInitialSize);\n\n  buf.appendInt8(-1);\n  buf.appendInt16(-2);\n  buf.appendInt32(-3);\n  BOOST_CHECK_EQUAL(buf.readableBytes(), 7);\n  BOOST_CHECK_EQUAL(buf.readInt8(), -1);\n  BOOST_CHECK_EQUAL(buf.readInt16(), -2);\n  BOOST_CHECK_EQUAL(buf.readInt32(), -3);\n}\n\nBOOST_AUTO_TEST_CASE(testBufferFindEOL)\n{\n  Buffer buf;\n  buf.append(string(100000, 'x'));\n  const char* null = NULL;\n  BOOST_CHECK_EQUAL(buf.findEOL(), null);\n  BOOST_CHECK_EQUAL(buf.findEOL(buf.peek()+90000), null);\n}\n\nvoid output(Buffer&& buf, const void* inner)\n{\n  Buffer newbuf(std::move(buf));\n  // printf(\"New Buffer at %p, inner %p\\n\", &newbuf, newbuf.peek());\n  BOOST_CHECK_EQUAL(inner, newbuf.peek());\n}\n\n// NOTE: This test fails in g++ 4.4, passes in g++ 4.6.\nBOOST_AUTO_TEST_CASE(testMove)\n{\n  Buffer buf;\n  buf.append(\"muduo\", 5);\n  const void* inner = buf.peek();\n  // printf(\"Buffer at %p, inner %p\\n\", &buf, inner);\n  output(std::move(buf), inner);\n}\n"
  },
  {
    "path": "muduo/net/tests/CMakeLists.txt",
    "content": "add_executable(channel_test Channel_test.cc)\ntarget_link_libraries(channel_test muduo_net)\n\nadd_executable(echoserver_unittest EchoServer_unittest.cc)\ntarget_link_libraries(echoserver_unittest muduo_net)\n\nadd_executable(echoclient_unittest EchoClient_unittest.cc)\ntarget_link_libraries(echoclient_unittest muduo_net)\n\nadd_executable(eventloop_unittest EventLoop_unittest.cc)\ntarget_link_libraries(eventloop_unittest muduo_net)\n\nadd_executable(eventloopthread_unittest EventLoopThread_unittest.cc)\ntarget_link_libraries(eventloopthread_unittest muduo_net)\n\nadd_executable(eventloopthreadpool_unittest EventLoopThreadPool_unittest.cc)\ntarget_link_libraries(eventloopthreadpool_unittest muduo_net)\n\nif(BOOSTTEST_LIBRARY)\nadd_executable(buffer_unittest Buffer_unittest.cc)\ntarget_link_libraries(buffer_unittest muduo_net boost_unit_test_framework)\nadd_test(NAME buffer_unittest COMMAND buffer_unittest)\n\nadd_executable(inetaddress_unittest InetAddress_unittest.cc)\ntarget_link_libraries(inetaddress_unittest muduo_net boost_unit_test_framework)\nadd_test(NAME inetaddress_unittest COMMAND inetaddress_unittest)\n\nif(ZLIB_FOUND)\n  add_executable(zlibstream_unittest ZlibStream_unittest.cc)\n  target_link_libraries(zlibstream_unittest muduo_net boost_unit_test_framework z)\n  # set_target_properties(zlibstream_unittest PROPERTIES COMPILE_FLAGS \"-std=c++0x\")\nendif()\n\nendif()\n\nadd_executable(tcpclient_reg1 TcpClient_reg1.cc)\ntarget_link_libraries(tcpclient_reg1 muduo_net)\n\nadd_executable(tcpclient_reg2 TcpClient_reg2.cc)\ntarget_link_libraries(tcpclient_reg2 muduo_net)\n\nadd_executable(tcpclient_reg3 TcpClient_reg3.cc)\ntarget_link_libraries(tcpclient_reg3 muduo_net)\n\nadd_executable(timerqueue_unittest TimerQueue_unittest.cc)\ntarget_link_libraries(timerqueue_unittest muduo_net)\nadd_test(NAME timerqueue_unittest COMMAND timerqueue_unittest)\n\n"
  },
  {
    "path": "muduo/net/tests/Channel_test.cc",
    "content": "#include \"muduo/base/Logging.h\"\n#include \"muduo/net/Channel.h\"\n#include \"muduo/net/EventLoop.h\"\n\n#include <functional>\n#include <map>\n\n#include <stdio.h>\n#include <unistd.h>\n#include <sys/timerfd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid print(const char* msg)\n{\n  static std::map<const char*, Timestamp> lasts;\n  Timestamp& last = lasts[msg];\n  Timestamp now = Timestamp::now();\n  printf(\"%s tid %d %s delay %f\\n\", now.toString().c_str(), CurrentThread::tid(),\n         msg, timeDifference(now, last));\n  last = now;\n}\n\nnamespace muduo\n{\nnamespace net\n{\nnamespace detail\n{\nint createTimerfd();\nvoid readTimerfd(int timerfd, Timestamp now);\n}\n}\n}\n\n// Use relative time, immunized to wall clock changes.\nclass PeriodicTimer\n{\n public:\n  PeriodicTimer(EventLoop* loop, double interval, const TimerCallback& cb)\n    : loop_(loop),\n      timerfd_(muduo::net::detail::createTimerfd()),\n      timerfdChannel_(loop, timerfd_),\n      interval_(interval),\n      cb_(cb)\n  {\n    timerfdChannel_.setReadCallback(\n        std::bind(&PeriodicTimer::handleRead, this));\n    timerfdChannel_.enableReading();\n  }\n\n  void start()\n  {\n    struct itimerspec spec;\n    memZero(&spec, sizeof spec);\n    spec.it_interval = toTimeSpec(interval_);\n    spec.it_value = spec.it_interval;\n    int ret = ::timerfd_settime(timerfd_, 0 /* relative timer */, &spec, NULL);\n    if (ret)\n    {\n      LOG_SYSERR << \"timerfd_settime()\";\n    }\n  }\n\n  ~PeriodicTimer()\n  {\n    timerfdChannel_.disableAll();\n    timerfdChannel_.remove();\n    ::close(timerfd_);\n  }\n\n private:\n  void handleRead()\n  {\n    loop_->assertInLoopThread();\n    muduo::net::detail::readTimerfd(timerfd_, Timestamp::now());\n    if (cb_)\n      cb_();\n  }\n\n  static struct timespec toTimeSpec(double seconds)\n  {\n    struct timespec ts;\n    memZero(&ts, sizeof ts);\n    const int64_t kNanoSecondsPerSecond = 1000000000;\n    const int kMinInterval = 100000;\n    int64_t nanoseconds = static_cast<int64_t>(seconds * kNanoSecondsPerSecond);\n    if (nanoseconds < kMinInterval)\n      nanoseconds = kMinInterval;\n    ts.tv_sec = static_cast<time_t>(nanoseconds / kNanoSecondsPerSecond);\n    ts.tv_nsec = static_cast<long>(nanoseconds % kNanoSecondsPerSecond);\n    return ts;\n  }\n\n  EventLoop* loop_;\n  const int timerfd_;\n  Channel timerfdChannel_;\n  const double interval_; // in seconds\n  TimerCallback cb_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid()\n           << \" Try adjusting the wall clock, see what happens.\";\n  EventLoop loop;\n  PeriodicTimer timer(&loop, 1, std::bind(print, \"PeriodicTimer\"));\n  timer.start();\n  loop.runEvery(1, std::bind(print, \"EventLoop::runEvery\"));\n  loop.loop();\n}\n"
  },
  {
    "path": "muduo/net/tests/EchoClient_unittest.cc",
    "content": "#include \"muduo/net/TcpClient.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint numThreads = 0;\nclass EchoClient;\nstd::vector<std::unique_ptr<EchoClient>> clients;\nint current = 0;\n\nclass EchoClient : noncopyable\n{\n public:\n  EchoClient(EventLoop* loop, const InetAddress& listenAddr, const string& id)\n    : loop_(loop),\n      client_(loop, listenAddr, \"EchoClient\"+id)\n  {\n    client_.setConnectionCallback(\n        std::bind(&EchoClient::onConnection, this, _1));\n    client_.setMessageCallback(\n        std::bind(&EchoClient::onMessage, this, _1, _2, _3));\n    //client_.enableRetry();\n  }\n\n  void connect()\n  {\n    client_.connect();\n  }\n  // void stop();\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->localAddress().toIpPort() << \" -> \"\n        << conn->peerAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n\n    if (conn->connected())\n    {\n      ++current;\n      if (implicit_cast<size_t>(current) < clients.size())\n      {\n        clients[current]->connect();\n      }\n      LOG_INFO << \"*** connected \" << current;\n    }\n    conn->send(\"world\\n\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n  {\n    string msg(buf->retrieveAllAsString());\n    LOG_TRACE << conn->name() << \" recv \" << msg.size() << \" bytes at \" << time.toString();\n    if (msg == \"quit\\n\")\n    {\n      conn->send(\"bye\\n\");\n      conn->shutdown();\n    }\n    else if (msg == \"shutdown\\n\")\n    {\n      loop_->quit();\n    }\n    else\n    {\n      conn->send(msg);\n    }\n  }\n\n  EventLoop* loop_;\n  TcpClient client_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  if (argc > 1)\n  {\n    EventLoop loop;\n    bool ipv6 = argc > 3;\n    InetAddress serverAddr(argv[1], 2000, ipv6);\n\n    int n = 1;\n    if (argc > 2)\n    {\n      n = atoi(argv[2]);\n    }\n\n    clients.reserve(n);\n    for (int i = 0; i < n; ++i)\n    {\n      char buf[32];\n      snprintf(buf, sizeof buf, \"%d\", i+1);\n      clients.emplace_back(new EchoClient(&loop, serverAddr, buf));\n    }\n\n    clients[current]->connect();\n    loop.loop();\n  }\n  else\n  {\n    printf(\"Usage: %s host_ip [current#]\\n\", argv[0]);\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/tests/EchoServer_unittest.cc",
    "content": "#include \"muduo/net/TcpServer.h\"\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/InetAddress.h\"\n\n#include <utility>\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint numThreads = 0;\n\nclass EchoServer\n{\n public:\n  EchoServer(EventLoop* loop, const InetAddress& listenAddr)\n    : loop_(loop),\n      server_(loop, listenAddr, \"EchoServer\")\n  {\n    server_.setConnectionCallback(\n        std::bind(&EchoServer::onConnection, this, _1));\n    server_.setMessageCallback(\n        std::bind(&EchoServer::onMessage, this, _1, _2, _3));\n    server_.setThreadNum(numThreads);\n  }\n\n  void start()\n  {\n    server_.start();\n  }\n  // void stop();\n\n private:\n  void onConnection(const TcpConnectionPtr& conn)\n  {\n    LOG_TRACE << conn->peerAddress().toIpPort() << \" -> \"\n        << conn->localAddress().toIpPort() << \" is \"\n        << (conn->connected() ? \"UP\" : \"DOWN\");\n    LOG_INFO << conn->getTcpInfoString();\n\n    conn->send(\"hello\\n\");\n  }\n\n  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)\n  {\n    string msg(buf->retrieveAllAsString());\n    LOG_TRACE << conn->name() << \" recv \" << msg.size() << \" bytes at \" << time.toString();\n    if (msg == \"exit\\n\")\n    {\n      conn->send(\"bye\\n\");\n      conn->shutdown();\n    }\n    if (msg == \"quit\\n\")\n    {\n      loop_->quit();\n    }\n    conn->send(msg);\n  }\n\n  EventLoop* loop_;\n  TcpServer server_;\n};\n\nint main(int argc, char* argv[])\n{\n  LOG_INFO << \"pid = \" << getpid() << \", tid = \" << CurrentThread::tid();\n  LOG_INFO << \"sizeof TcpConnection = \" << sizeof(TcpConnection);\n  if (argc > 1)\n  {\n    numThreads = atoi(argv[1]);\n  }\n  bool ipv6 = argc > 2;\n  EventLoop loop;\n  InetAddress listenAddr(2000, false, ipv6);\n  EchoServer server(&loop, listenAddr);\n\n  server.start();\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "muduo/net/tests/EventLoopThreadPool_unittest.cc",
    "content": "#include \"muduo/net/EventLoopThreadPool.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid print(EventLoop* p = NULL)\n{\n  printf(\"main(): pid = %d, tid = %d, loop = %p\\n\",\n         getpid(), CurrentThread::tid(), p);\n}\n\nvoid init(EventLoop* p)\n{\n  printf(\"init(): pid = %d, tid = %d, loop = %p\\n\",\n         getpid(), CurrentThread::tid(), p);\n}\n\nint main()\n{\n  print();\n\n  EventLoop loop;\n  loop.runAfter(11, std::bind(&EventLoop::quit, &loop));\n\n  {\n    printf(\"Single thread %p:\\n\", &loop);\n    EventLoopThreadPool model(&loop, \"single\");\n    model.setThreadNum(0);\n    model.start(init);\n    assert(model.getNextLoop() == &loop);\n    assert(model.getNextLoop() == &loop);\n    assert(model.getNextLoop() == &loop);\n  }\n\n  {\n    printf(\"Another thread:\\n\");\n    EventLoopThreadPool model(&loop, \"another\");\n    model.setThreadNum(1);\n    model.start(init);\n    EventLoop* nextLoop = model.getNextLoop();\n    nextLoop->runAfter(2, std::bind(print, nextLoop));\n    assert(nextLoop != &loop);\n    assert(nextLoop == model.getNextLoop());\n    assert(nextLoop == model.getNextLoop());\n    ::sleep(3);\n  }\n\n  {\n    printf(\"Three threads:\\n\");\n    EventLoopThreadPool model(&loop, \"three\");\n    model.setThreadNum(3);\n    model.start(init);\n    EventLoop* nextLoop = model.getNextLoop();\n    nextLoop->runInLoop(std::bind(print, nextLoop));\n    assert(nextLoop != &loop);\n    assert(nextLoop != model.getNextLoop());\n    assert(nextLoop != model.getNextLoop());\n    assert(nextLoop == model.getNextLoop());\n  }\n\n  loop.loop();\n}\n\n"
  },
  {
    "path": "muduo/net/tests/EventLoopThread_unittest.cc",
    "content": "#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/base/CountDownLatch.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid print(EventLoop* p = NULL)\n{\n  printf(\"print: pid = %d, tid = %d, loop = %p\\n\",\n         getpid(), CurrentThread::tid(), p);\n}\n\nvoid quit(EventLoop* p)\n{\n  print(p);\n  p->quit();\n}\n\nint main()\n{\n  print();\n\n  {\n  EventLoopThread thr1;  // never start\n  }\n\n  {\n  // dtor calls quit()\n  EventLoopThread thr2;\n  EventLoop* loop = thr2.startLoop();\n  loop->runInLoop(std::bind(print, loop));\n  CurrentThread::sleepUsec(500 * 1000);\n  }\n\n  {\n  // quit() before dtor\n  EventLoopThread thr3;\n  EventLoop* loop = thr3.startLoop();\n  loop->runInLoop(std::bind(quit, loop));\n  CurrentThread::sleepUsec(500 * 1000);\n  }\n}\n\n"
  },
  {
    "path": "muduo/net/tests/EventLoop_unittest.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <assert.h>\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nEventLoop* g_loop;\n\nvoid callback()\n{\n  printf(\"callback(): pid = %d, tid = %d\\n\", getpid(), CurrentThread::tid());\n  EventLoop anotherLoop;\n}\n\nvoid threadFunc()\n{\n  printf(\"threadFunc(): pid = %d, tid = %d\\n\", getpid(), CurrentThread::tid());\n\n  assert(EventLoop::getEventLoopOfCurrentThread() == NULL);\n  EventLoop loop;\n  assert(EventLoop::getEventLoopOfCurrentThread() == &loop);\n  loop.runAfter(1.0, callback);\n  loop.loop();\n}\n\nint main()\n{\n  printf(\"main(): pid = %d, tid = %d\\n\", getpid(), CurrentThread::tid());\n\n  assert(EventLoop::getEventLoopOfCurrentThread() == NULL);\n  EventLoop loop;\n  assert(EventLoop::getEventLoopOfCurrentThread() == &loop);\n\n  Thread thread(threadFunc);\n  thread.start();\n\n  loop.loop();\n}\n"
  },
  {
    "path": "muduo/net/tests/InetAddress_unittest.cc",
    "content": "#include \"muduo/net/InetAddress.h\"\n\n#include \"muduo/base/Logging.h\"\n\n//#define BOOST_TEST_MODULE InetAddressTest\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\nusing muduo::string;\nusing muduo::net::InetAddress;\n\nBOOST_AUTO_TEST_CASE(testInetAddress)\n{\n  InetAddress addr0(1234);\n  BOOST_CHECK_EQUAL(addr0.toIp(), string(\"0.0.0.0\"));\n  BOOST_CHECK_EQUAL(addr0.toIpPort(), string(\"0.0.0.0:1234\"));\n  BOOST_CHECK_EQUAL(addr0.port(), 1234);\n\n  InetAddress addr1(4321, true);\n  BOOST_CHECK_EQUAL(addr1.toIp(), string(\"127.0.0.1\"));\n  BOOST_CHECK_EQUAL(addr1.toIpPort(), string(\"127.0.0.1:4321\"));\n  BOOST_CHECK_EQUAL(addr1.port(), 4321);\n\n  InetAddress addr2(\"1.2.3.4\", 8888);\n  BOOST_CHECK_EQUAL(addr2.toIp(), string(\"1.2.3.4\"));\n  BOOST_CHECK_EQUAL(addr2.toIpPort(), string(\"1.2.3.4:8888\"));\n  BOOST_CHECK_EQUAL(addr2.port(), 8888);\n\n  InetAddress addr3(\"255.254.253.252\", 65535);\n  BOOST_CHECK_EQUAL(addr3.toIp(), string(\"255.254.253.252\"));\n  BOOST_CHECK_EQUAL(addr3.toIpPort(), string(\"255.254.253.252:65535\"));\n  BOOST_CHECK_EQUAL(addr3.port(), 65535);\n}\n\nBOOST_AUTO_TEST_CASE(testInet6Address)\n{\n  InetAddress addr0(1234, false, true);\n  BOOST_CHECK_EQUAL(addr0.toIp(), string(\"::\"));\n  BOOST_CHECK_EQUAL(addr0.toIpPort(), string(\"[::]:1234\"));\n  BOOST_CHECK_EQUAL(addr0.port(), 1234);\n\n  InetAddress addr1(1234, true, true);\n  BOOST_CHECK_EQUAL(addr1.toIp(), string(\"::1\"));\n  BOOST_CHECK_EQUAL(addr1.toIpPort(), string(\"[::1]:1234\"));\n  BOOST_CHECK_EQUAL(addr1.port(), 1234);\n\n  InetAddress addr2(\"2001:db8::1\", 8888, true);\n  BOOST_CHECK_EQUAL(addr2.toIp(), string(\"2001:db8::1\"));\n  BOOST_CHECK_EQUAL(addr2.toIpPort(), string(\"[2001:db8::1]:8888\"));\n  BOOST_CHECK_EQUAL(addr2.port(), 8888);\n\n  InetAddress addr3(\"fe80::1234:abcd:1\", 8888);\n  BOOST_CHECK_EQUAL(addr3.toIp(), string(\"fe80::1234:abcd:1\"));\n  BOOST_CHECK_EQUAL(addr3.toIpPort(), string(\"[fe80::1234:abcd:1]:8888\"));\n  BOOST_CHECK_EQUAL(addr3.port(), 8888);\n}\n\nBOOST_AUTO_TEST_CASE(testInetAddressResolve)\n{\n  InetAddress addr(80);\n  if (InetAddress::resolve(\"google.com\", &addr))\n  {\n    LOG_INFO << \"google.com resolved to \" << addr.toIpPort();\n  }\n  else\n  {\n    LOG_ERROR << \"Unable to resolve google.com\";\n  }\n}\n"
  },
  {
    "path": "muduo/net/tests/TcpClient_reg1.cc",
    "content": "// TcpClient::stop() called in the same iteration of IO event\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nTcpClient* g_client;\n\nvoid timeout()\n{\n  LOG_INFO << \"timeout\";\n  g_client->stop();\n}\n\nint main(int argc, char* argv[])\n{\n  EventLoop loop;\n  InetAddress serverAddr(\"127.0.0.1\", 2); // no such server\n  TcpClient client(&loop, serverAddr, \"TcpClient\");\n  g_client = &client;\n  loop.runAfter(0.0, timeout);\n  loop.runAfter(1.0, std::bind(&EventLoop::quit, &loop));\n  client.connect();\n  CurrentThread::sleepUsec(100 * 1000);\n  loop.loop();\n}\n"
  },
  {
    "path": "muduo/net/tests/TcpClient_reg2.cc",
    "content": "// TcpClient destructs when TcpConnection is connected but unique.\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/base/Thread.h\"\n#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/TcpClient.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nvoid threadFunc(EventLoop* loop)\n{\n  InetAddress serverAddr(\"127.0.0.1\", 1234); // should succeed\n  TcpClient client(loop, serverAddr, \"TcpClient\");\n  client.connect();\n\n  CurrentThread::sleepUsec(1000*1000);\n  // client destructs when connected.\n}\n\nint main(int argc, char* argv[])\n{\n  Logger::setLogLevel(Logger::DEBUG);\n\n  EventLoop loop;\n  loop.runAfter(3.0, std::bind(&EventLoop::quit, &loop));\n  Thread thr(std::bind(threadFunc, &loop));\n  thr.start();\n  loop.loop();\n}\n"
  },
  {
    "path": "muduo/net/tests/TcpClient_reg3.cc",
    "content": "// TcpClient destructs in a different thread.\n\n#include \"muduo/base/Logging.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/net/TcpClient.h\"\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint main(int argc, char* argv[])\n{\n  Logger::setLogLevel(Logger::DEBUG);\n\n  EventLoopThread loopThread;\n  {\n  InetAddress serverAddr(\"127.0.0.1\", 1234); // should succeed\n  TcpClient client(loopThread.startLoop(), serverAddr, \"TcpClient\");\n  client.connect();\n  CurrentThread::sleepUsec(500 * 1000);  // wait for connect\n  client.disconnect();\n  }\n\n  CurrentThread::sleepUsec(1000 * 1000);\n}\n"
  },
  {
    "path": "muduo/net/tests/TimerQueue_unittest.cc",
    "content": "#include \"muduo/net/EventLoop.h\"\n#include \"muduo/net/EventLoopThread.h\"\n#include \"muduo/base/Thread.h\"\n\n#include <stdio.h>\n#include <unistd.h>\n\nusing namespace muduo;\nusing namespace muduo::net;\n\nint cnt = 0;\nEventLoop* g_loop;\n\nvoid printTid()\n{\n  printf(\"pid = %d, tid = %d\\n\", getpid(), CurrentThread::tid());\n  printf(\"now %s\\n\", Timestamp::now().toString().c_str());\n}\n\nvoid print(const char* msg)\n{\n  printf(\"msg %s %s\\n\", Timestamp::now().toString().c_str(), msg);\n  if (++cnt == 20)\n  {\n    g_loop->quit();\n  }\n}\n\nvoid cancel(TimerId timer)\n{\n  g_loop->cancel(timer);\n  printf(\"cancelled at %s\\n\", Timestamp::now().toString().c_str());\n}\n\nint main()\n{\n  printTid();\n  sleep(1);\n  {\n    EventLoop loop;\n    g_loop = &loop;\n\n    print(\"main\");\n    loop.runAfter(1, std::bind(print, \"once1\"));\n    loop.runAfter(1.5, std::bind(print, \"once1.5\"));\n    loop.runAfter(2.5, std::bind(print, \"once2.5\"));\n    loop.runAfter(3.5, std::bind(print, \"once3.5\"));\n    TimerId t45 = loop.runAfter(4.5, std::bind(print, \"once4.5\"));\n    loop.runAfter(4.2, std::bind(cancel, t45));\n    loop.runAfter(4.8, std::bind(cancel, t45));\n    loop.runEvery(2, std::bind(print, \"every2\"));\n    TimerId t3 = loop.runEvery(3, std::bind(print, \"every3\"));\n    loop.runAfter(9.001, std::bind(cancel, t3));\n\n    loop.loop();\n    print(\"main loop exits\");\n  }\n  sleep(1);\n  {\n    EventLoopThread loopThread;\n    EventLoop* loop = loopThread.startLoop();\n    loop->runAfter(2, printTid);\n    sleep(3);\n    print(\"thread loop exits\");\n  }\n}\n"
  },
  {
    "path": "muduo/net/tests/ZlibStream_unittest.cc",
    "content": "#include \"muduo/net/ZlibStream.h\"\n\n#include \"muduo/base/Logging.h\"\n\n#define BOOST_TEST_MAIN\n#define BOOST_TEST_DYN_LINK\n#include <boost/test/unit_test.hpp>\n\n#include <stdio.h>\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream)\n{\n  muduo::net::Buffer output;\n  {\n    muduo::net::ZlibOutputStream stream(&output);\n    BOOST_CHECK_EQUAL(output.readableBytes(), 0);\n  }\n  BOOST_CHECK_EQUAL(output.readableBytes(), 8);\n}\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream1)\n{\n  muduo::net::Buffer output;\n  muduo::net::ZlibOutputStream stream(&output);\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_OK);\n  stream.finish();\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_STREAM_END);\n}\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream2)\n{\n  muduo::net::Buffer output;\n  muduo::net::ZlibOutputStream stream(&output);\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_OK);\n  BOOST_CHECK(stream.write(\"01234567890123456789012345678901234567890123456789\"));\n  stream.finish();\n  // printf(\"%zd\\n\", output.readableBytes());\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_STREAM_END);\n}\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream3)\n{\n  muduo::net::Buffer output;\n  muduo::net::ZlibOutputStream stream(&output);\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_OK);\n  for (int i = 0; i < 1024*1024; ++i)\n  {\n    BOOST_CHECK(stream.write(\"01234567890123456789012345678901234567890123456789\"));\n  }\n  stream.finish();\n  // printf(\"total %zd\\n\", output.readableBytes());\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_STREAM_END);\n}\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream4)\n{\n  muduo::net::Buffer output;\n  muduo::net::ZlibOutputStream stream(&output);\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_OK);\n  muduo::string input;\n  for (int i = 0; i < 32768; ++i)\n  {\n    input += \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-\"[rand() % 64];\n  }\n\n  for (int i = 0; i < 10; ++i)\n  {\n    BOOST_CHECK(stream.write(input));\n  }\n  stream.finish();\n  // printf(\"total %zd\\n\", output.readableBytes());\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_STREAM_END);\n}\n\nBOOST_AUTO_TEST_CASE(testZlibOutputStream5)\n{\n  muduo::net::Buffer output;\n  muduo::net::ZlibOutputStream stream(&output);\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_OK);\n  muduo::string input(1024*1024, '_');\n  for (int i = 0; i < 64; ++i)\n  {\n    BOOST_CHECK(stream.write(input));\n  }\n  printf(\"bufsiz %d\\n\", stream.internalOutputBufferSize());\n  LOG_INFO << \"total_in \" << stream.inputBytes();\n  LOG_INFO << \"total_out \" << stream.outputBytes();\n  stream.finish();\n  printf(\"total %zd\\n\", output.readableBytes());\n  BOOST_CHECK_EQUAL(stream.zlibErrorCode(), Z_STREAM_END);\n}\n"
  },
  {
    "path": "patches/MacOSX.diff",
    "content": "diff --git a/CMakeLists.txt b/CMakeLists.txt\nindex d18840f..8c9f075 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -11,14 +11,15 @@ endif()\n set(CXX_FLAGS\n  -g\n  # -DVALGRIND\n- # -DMUDUO_STD_STRING\n- -DCHECK_PTHREAD_RETURN_VALUE\n+ # -DCHECK_PTHREAD_RETURN_VALUE\n+ -DMUDUO_STD_STRING\n  -D_FILE_OFFSET_BITS=64\n  -Wall\n  -Wextra\n- -Werror\n+ # -Werror\n  -Wconversion\n  -Wno-unused-parameter\n+ -Wno-sign-conversion\n  -Wold-style-cast\n  -Woverloaded-virtual\n  -Wpointer-arith\n@@ -27,16 +28,15 @@ set(CXX_FLAGS\n  -march=native\n  # -MMD\n  # -std=c++0x\n- -rdynamic\n  )\n if(CMAKE_BUILD_BITS EQUAL 32)\n   list(APPEND CXX_FLAGS \"-m32\")\n endif()\n string(REPLACE \";\" \" \" CMAKE_CXX_FLAGS \"${CXX_FLAGS}\")\n \n-set(CMAKE_CXX_COMPILER \"g++\")\n+set(CMAKE_CXX_COMPILER \"clang++\")\n set(CMAKE_CXX_FLAGS_DEBUG \"-O0\")\n-set(CMAKE_CXX_FLAGS_RELEASE \"-O2 -finline-limit=1000 -DNDEBUG\")\n+set(CMAKE_CXX_FLAGS_RELEASE \"-O2 -DNDEBUG\")\n set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)\n set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)\n \ndiff --git a/examples/roundtrip/roundtrip_udp.cc b/examples/roundtrip/roundtrip_udp.cc\nindex 5f171b8..d612570 100644\n--- a/examples/roundtrip/roundtrip_udp.cc\n+++ b/examples/roundtrip/roundtrip_udp.cc\n@@ -17,7 +17,12 @@ const size_t frameLen = 2*sizeof(int64_t);\n \n int createNonblockingUDP()\n {\n+#ifndef  __MACH__\n   int sockfd = ::socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_UDP);\n+#else\n+  int sockfd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);\n+  sockets::setNonBlockAndCloseOnExec(sockfd);\n+#endif\n   if (sockfd < 0)\n   {\n     LOG_SYSFATAL << \"::socket\";\ndiff --git a/examples/socks4a/tcprelay.cc b/examples/socks4a/tcprelay.cc\nindex a4c6ec9..09a6a3a 100644\n--- a/examples/socks4a/tcprelay.cc\n+++ b/examples/socks4a/tcprelay.cc\n@@ -1,6 +1,5 @@\n #include \"tunnel.h\"\n \n-#include <malloc.h>\n #include <stdio.h>\n #include <sys/resource.h>\n \n@@ -43,7 +42,6 @@ void onServerMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)\n \n void memstat()\n {\n-  malloc_stats();\n }\n \n int main(int argc, char* argv[])\ndiff --git a/muduo/base/CMakeLists.txt b/muduo/base/CMakeLists.txt\nindex 6e250d4..a76404f 100644\n--- a/muduo/base/CMakeLists.txt\n+++ b/muduo/base/CMakeLists.txt\n@@ -16,10 +16,10 @@ set(base_SRCS\n   )\n \n add_library(muduo_base ${base_SRCS})\n-target_link_libraries(muduo_base pthread rt)\n+target_link_libraries(muduo_base pthread)\n \n add_library(muduo_base_cpp11 ${base_SRCS})\n-target_link_libraries(muduo_base_cpp11 pthread rt)\n+target_link_libraries(muduo_base_cpp11 pthread)\n set_target_properties(muduo_base_cpp11 PROPERTIES COMPILE_FLAGS \"-std=c++0x\")\n \n install(TARGETS muduo_base DESTINATION lib)\ndiff --git a/muduo/base/Condition.cc b/muduo/base/Condition.cc\nindex f10ace3..73a1715 100644\n--- a/muduo/base/Condition.cc\n+++ b/muduo/base/Condition.cc\n@@ -6,13 +6,21 @@\n #include <muduo/base/Condition.h>\n \n #include <errno.h>\n+#include <sys/time.h>\n \n // returns true if time out, false otherwise.\n bool muduo::Condition::waitForSeconds(int seconds)\n {\n   struct timespec abstime;\n+#ifdef CLOCK_REALTIME\n   // FIXME: use CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW to prevent time rewind.\n   clock_gettime(CLOCK_REALTIME, &abstime);\n+#else  // Mac OS X\n+  struct timeval tv;\n+  gettimeofday(&tv, NULL);\n+  abstime.tv_sec = tv.tv_sec;\n+  abstime.tv_nsec = tv.tv_usec * 1000;\n+#endif\n   abstime.tv_sec += seconds;\n   MutexLock::UnassignGuard ug(mutex_);\n   return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime);\ndiff --git a/muduo/base/FileUtil.cc b/muduo/base/FileUtil.cc\nindex 999b0e5..ad9da8a 100644\n--- a/muduo/base/FileUtil.cc\n+++ b/muduo/base/FileUtil.cc\n@@ -64,8 +64,12 @@ void FileUtil::AppendFile::flush()\n \n size_t FileUtil::AppendFile::write(const char* logline, size_t len)\n {\n+#ifdef fwrite_unlocked\n   // #undef fwrite_unlocked\n   return ::fwrite_unlocked(logline, 1, len, fp_);\n+#else\n+  return ::fwrite(logline, 1, len, fp_);\n+#endif\n }\n \n FileUtil::ReadSmallFile::ReadSmallFile(StringArg filename)\ndiff --git a/muduo/base/Logging.cc b/muduo/base/Logging.cc\nindex 341d627..9376007 100644\n--- a/muduo/base/Logging.cc\n+++ b/muduo/base/Logging.cc\n@@ -36,7 +36,12 @@ __thread time_t t_lastSecond;\n \n const char* strerror_tl(int savedErrno)\n {\n+#ifndef __MACH__\n   return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);\n+#else\n+  strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);\n+  return t_errnobuf;\n+#endif\n }\n \n Logger::LogLevel initLogLevel()\ndiff --git a/muduo/base/Thread.cc b/muduo/base/Thread.cc\nindex 9d64780..cddcac1 100644\n--- a/muduo/base/Thread.cc\n+++ b/muduo/base/Thread.cc\n@@ -15,10 +15,12 @@\n #include <errno.h>\n #include <stdio.h>\n #include <unistd.h>\n-#include <sys/prctl.h>\n #include <sys/syscall.h>\n #include <sys/types.h>\n+#ifndef __MACH__\n+#include <sys/prctl.h>\n #include <linux/unistd.h>\n+#endif\n \n namespace muduo\n {\n@@ -35,10 +37,17 @@ namespace CurrentThread\n namespace detail\n {\n \n+#ifdef __MACH__\n+pid_t gettid()\n+{\n+  return pthread_mach_thread_np(pthread_self());\n+}\n+#else\n pid_t gettid()\n {\n   return static_cast<pid_t>(::syscall(SYS_gettid));\n }\n+#endif\n \n void afterFork()\n {\n@@ -88,7 +97,9 @@ struct ThreadData\n     }\n \n     muduo::CurrentThread::t_threadName = name_.empty() ? \"muduoThread\" : name_.c_str();\n+#ifndef __MACH__\n     ::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);\n+#endif\n     try\n     {\n       func_();\ndiff --git a/muduo/base/TimeZone.cc b/muduo/base/TimeZone.cc\nindex 37959d9..f95beb3 100644\n--- a/muduo/base/TimeZone.cc\n+++ b/muduo/base/TimeZone.cc\n@@ -8,7 +8,7 @@\n #include <vector>\n \n //#define _BSD_SOURCE\n-#include <endian.h>\n+#include <muduo/net/Endian.h>\n \n #include <stdint.h>\n #include <stdio.h>\n@@ -285,7 +285,7 @@ struct tm TimeZone::toLocalTime(time_t seconds) const\n     ::gmtime_r(&localSeconds, &localTime); // FIXME: fromUtcTime\n     localTime.tm_isdst = local->isDst;\n     localTime.tm_gmtoff = local->gmtOffset;\n-    localTime.tm_zone = &data.abbreviation[local->arrbIdx];\n+    localTime.tm_zone = const_cast<char*>(&data.abbreviation[local->arrbIdx]);\n   }\n \n   return localTime;\ndiff --git a/muduo/base/tests/AsyncLogging_test.cc b/muduo/base/tests/AsyncLogging_test.cc\nindex bd9fe59..e510fd4 100644\n--- a/muduo/base/tests/AsyncLogging_test.cc\n+++ b/muduo/base/tests/AsyncLogging_test.cc\n@@ -4,6 +4,9 @@\n \n #include <stdio.h>\n #include <sys/resource.h>\n+#ifdef __MACH__\n+#include <libgen.h>  // basename()\n+#endif\n \n int kRollSize = 500*1000*1000;\n \ndiff --git a/muduo/base/tests/BlockingQueue_test.cc b/muduo/base/tests/BlockingQueue_test.cc\nindex c392773..6977578 100644\n--- a/muduo/base/tests/BlockingQueue_test.cc\n+++ b/muduo/base/tests/BlockingQueue_test.cc\n@@ -80,9 +80,6 @@ class Test\n void testMove()\n {\n #ifdef __GXX_EXPERIMENTAL_CXX0X__\n-\n-// std::unique_ptr requires gcc 4.4 or later\n-#if __GNUC_PREREQ (4,4)\n   muduo::BlockingQueue<std::unique_ptr<int>> queue;\n   queue.put(std::unique_ptr<int>(new int(42)));\n   std::unique_ptr<int> x = queue.take();\n@@ -92,8 +89,6 @@ void testMove()\n   std::unique_ptr<int> y = queue.take();\n   printf(\"took %d\\n\", *y);\n #endif\n-\n-#endif\n }\n \n int main()\ndiff --git a/muduo/base/tests/GzipFile_test.cc b/muduo/base/tests/GzipFile_test.cc\nindex 6dc0d4d..b051ca8 100644\n--- a/muduo/base/tests/GzipFile_test.cc\n+++ b/muduo/base/tests/GzipFile_test.cc\n@@ -2,6 +2,8 @@\n \n #include <muduo/base/Logging.h>\n \n+#include <errno.h>\n+\n int main()\n {\n   const char* filename = \"/tmp/gzipfile_test.gz\";\ndiff --git a/muduo/base/tests/LogFile_test.cc b/muduo/base/tests/LogFile_test.cc\nindex e77d68d..d27d65e 100644\n--- a/muduo/base/tests/LogFile_test.cc\n+++ b/muduo/base/tests/LogFile_test.cc\n@@ -1,5 +1,8 @@\n #include <muduo/base/LogFile.h>\n #include <muduo/base/Logging.h>\n+#ifdef __MACH__\n+#include <libgen.h>  // basename()\n+#endif\n \n boost::scoped_ptr<muduo::LogFile> g_logFile;\n \ndiff --git a/muduo/net/CMakeLists.txt b/muduo/net/CMakeLists.txt\nindex 0127c48..9ea16ed 100644\n--- a/muduo/net/CMakeLists.txt\n+++ b/muduo/net/CMakeLists.txt\n@@ -16,7 +16,6 @@ set(net_SRCS\n   InetAddress.cc\n   Poller.cc\n   poller/DefaultPoller.cc\n-  poller/EPollPoller.cc\n   poller/PollPoller.cc\n   Socket.cc\n   SocketsOps.cc\ndiff --git a/muduo/net/Channel.cc b/muduo/net/Channel.cc\nindex f5e6624..62fbd6f 100644\n--- a/muduo/net/Channel.cc\n+++ b/muduo/net/Channel.cc\n@@ -102,6 +102,9 @@ void Channel::handleEventWithGuard(Timestamp receiveTime)\n   {\n     if (errorCallback_) errorCallback_();\n   }\n+#ifndef POLLRDHUP\n+  const int POLLRDHUP = 0;\n+#endif\n   if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))\n   {\n     if (readCallback_) readCallback_(receiveTime);\n@@ -135,8 +138,10 @@ string Channel::eventsToString(int fd, int ev)\n     oss << \"OUT \";\n   if (ev & POLLHUP)\n     oss << \"HUP \";\n+#ifdef POLLRDHUP\n   if (ev & POLLRDHUP)\n     oss << \"RDHUP \";\n+#endif\n   if (ev & POLLERR)\n     oss << \"ERR \";\n   if (ev & POLLNVAL)\ndiff --git a/muduo/net/Endian.h b/muduo/net/Endian.h\nindex b277503..851e449 100644\n--- a/muduo/net/Endian.h\n+++ b/muduo/net/Endian.h\n@@ -12,7 +12,28 @@\n #define MUDUO_NET_ENDIAN_H\n \n #include <stdint.h>\n+\n+#ifdef __MACH__\n+#include <libkern/OSByteOrder.h>\n+ \n+#define htobe16(x) OSSwapHostToBigInt16(x)\n+#define htole16(x) OSSwapHostToLittleInt16(x)\n+#define be16toh(x) OSSwapBigToHostInt16(x)\n+#define le16toh(x) OSSwapLittleToHostInt16(x)\n+ \n+#define htobe32(x) OSSwapHostToBigInt32(x)\n+#define htole32(x) OSSwapHostToLittleInt32(x)\n+#define be32toh(x) OSSwapBigToHostInt32(x)\n+#define le32toh(x) OSSwapLittleToHostInt32(x)\n+ \n+#define htobe64(x) OSSwapHostToBigInt64(x)\n+#define htole64(x) OSSwapHostToLittleInt64(x)\n+#define be64toh(x) OSSwapBigToHostInt64(x)\n+#define le64toh(x) OSSwapLittleToHostInt64(x)\n+#else\n #include <endian.h>\n+#endif\n+\n \n namespace muduo\n {\n@@ -60,8 +81,8 @@ inline uint16_t networkToHost16(uint16_t net16)\n #if defined(__clang__) || __GNUC_MINOR__ >= 6\n #pragma GCC diagnostic pop\n #else\n-#pragma GCC diagnostic warning \"-Wconversion\"\n-#pragma GCC diagnostic warning \"-Wold-style-cast\"\n+//#pragma GCC diagnostic error \"-Wconversion\"\n+//#pragma GCC diagnostic error \"-Wold-style-cast\"\n #endif\n \n \ndiff --git a/muduo/net/EventLoop.cc b/muduo/net/EventLoop.cc\nindex 7346838..19fde05 100644\n--- a/muduo/net/EventLoop.cc\n+++ b/muduo/net/EventLoop.cc\n@@ -18,7 +18,8 @@\n #include <boost/bind.hpp>\n \n #include <signal.h>\n-#include <sys/eventfd.h>\n+#include <sys/types.h>\n+#include <sys/socket.h>\n \n using namespace muduo;\n using namespace muduo::net;\n@@ -29,18 +30,6 @@ __thread EventLoop* t_loopInThisThread = 0;\n \n const int kPollTimeMs = 10000;\n \n-int createEventfd()\n-{\n-  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\n-  if (evtfd < 0)\n-  {\n-    LOG_SYSERR << \"Failed in eventfd\";\n-    abort();\n-  }\n-  return evtfd;\n-}\n-\n-#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n class IgnoreSigPipe\n {\n  public:\n@@ -50,7 +39,6 @@ class IgnoreSigPipe\n     // LOG_TRACE << \"Ignore SIGPIPE\";\n   }\n };\n-#pragma GCC diagnostic error \"-Wold-style-cast\"\n \n IgnoreSigPipe initObj;\n }\n@@ -69,11 +57,15 @@ EventLoop::EventLoop()\n     threadId_(CurrentThread::tid()),\n     poller_(Poller::newDefaultPoller(this)),\n     timerQueue_(new TimerQueue(this)),\n-    wakeupFd_(createEventfd()),\n-    wakeupChannel_(new Channel(this, wakeupFd_)),\n     currentActiveChannel_(NULL)\n {\n   LOG_DEBUG << \"EventLoop created \" << this << \" in thread \" << threadId_;\n+  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, wakeupFd_) < 0)\n+  {\n+    LOG_SYSFATAL << \"Failed in socketpair\";\n+  }\n+  wakeupChannel_.reset(new Channel(this, wakeupFd_[0]));\n+\n   if (t_loopInThisThread)\n   {\n     LOG_FATAL << \"Another EventLoop \" << t_loopInThisThread\n@@ -95,7 +87,8 @@ EventLoop::~EventLoop()\n             << \" destructs in thread \" << CurrentThread::tid();\n   wakeupChannel_->disableAll();\n   wakeupChannel_->remove();\n-  ::close(wakeupFd_);\n+  ::close(wakeupFd_[0]);\n+  ::close(wakeupFd_[1]);\n   t_loopInThisThread = NULL;\n }\n \n@@ -110,12 +103,13 @@ void EventLoop::loop()\n   while (!quit_)\n   {\n     activeChannels_.clear();\n-    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);\n+    pollReturnTime_ = poller_->poll(timerQueue_->getTimeout(), &activeChannels_);\n     ++iteration_;\n     if (Logger::logLevel() <= Logger::TRACE)\n     {\n       printActiveChannels();\n     }\n+    timerQueue_->processTimers();\n     // TODO sort channel by priority\n     eventHandling_ = true;\n     for (ChannelList::iterator it = activeChannels_.begin();\n@@ -273,7 +267,7 @@ void EventLoop::abortNotInLoopThread()\n void EventLoop::wakeup()\n {\n   uint64_t one = 1;\n-  ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);\n+  ssize_t n = sockets::write(wakeupFd_[1], &one, sizeof one);\n   if (n != sizeof one)\n   {\n     LOG_ERROR << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 8\";\n@@ -283,7 +277,7 @@ void EventLoop::wakeup()\n void EventLoop::handleRead()\n {\n   uint64_t one = 1;\n-  ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);\n+  ssize_t n = sockets::read(wakeupFd_[0], &one, sizeof one);\n   if (n != sizeof one)\n   {\n     LOG_ERROR << \"EventLoop::handleRead() reads \" << n << \" bytes instead of 8\";\ndiff --git a/muduo/net/EventLoop.h b/muduo/net/EventLoop.h\nindex 5741961..b03dd67 100644\n--- a/muduo/net/EventLoop.h\n+++ b/muduo/net/EventLoop.h\n@@ -156,7 +156,7 @@ class EventLoop : boost::noncopyable\n   Timestamp pollReturnTime_;\n   boost::scoped_ptr<Poller> poller_;\n   boost::scoped_ptr<TimerQueue> timerQueue_;\n-  int wakeupFd_;\n+  int wakeupFd_[2];\n   // unlike in TimerQueue, which is an internal class,\n   // we don't expose Channel to client.\n   boost::scoped_ptr<Channel> wakeupChannel_;\ndiff --git a/muduo/net/InetAddress.cc b/muduo/net/InetAddress.cc\nindex 394870a..05bb5de 100644\n--- a/muduo/net/InetAddress.cc\n+++ b/muduo/net/InetAddress.cc\n@@ -19,10 +19,10 @@\n #include <boost/static_assert.hpp>\n \n // INADDR_ANY use (type)value casting.\n-#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n+// #pragma GCC diagnostic ignored \"-Wold-style-cast\"\n static const in_addr_t kInaddrAny = INADDR_ANY;\n static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;\n-#pragma GCC diagnostic error \"-Wold-style-cast\"\n+// #pragma GCC diagnostic error \"-Wold-style-cast\"\n \n //     /* Structure describing an Internet socket address.  */\n //     struct sockaddr_in {\n@@ -83,10 +83,15 @@ bool InetAddress::resolve(StringArg hostname, InetAddress* out)\n   assert(out != NULL);\n   struct hostent hent;\n   struct hostent* he = NULL;\n-  int herrno = 0;\n   bzero(&hent, sizeof(hent));\n \n+#ifndef __MACH__\n+  int herrno = 0;\n   int ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno);\n+#else\n+  he = gethostbyname(hostname.c_str());\n+  int ret = 0;\n+#endif\n   if (ret == 0 && he != NULL)\n   {\n     assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));\ndiff --git a/muduo/net/Socket.cc b/muduo/net/Socket.cc\nindex 111d87d..4e0efa7 100644\n--- a/muduo/net/Socket.cc\n+++ b/muduo/net/Socket.cc\n@@ -27,13 +27,18 @@ Socket::~Socket()\n \n bool Socket::getTcpInfo(struct tcp_info* tcpi) const\n {\n+#ifndef __MACH__\n   socklen_t len = sizeof(*tcpi);\n   bzero(tcpi, len);\n   return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpi, &len) == 0;\n+#else\n+  return false;\n+#endif\n }\n \n bool Socket::getTcpInfoString(char* buf, int len) const\n {\n+#ifndef __MACH__\n   struct tcp_info tcpi;\n   bool ok = getTcpInfo(&tcpi);\n   if (ok)\n@@ -56,6 +61,9 @@ bool Socket::getTcpInfoString(char* buf, int len) const\n              tcpi.tcpi_total_retrans);  // Total retransmits for entire connection\n   }\n   return ok;\n+#else\n+  return false;\n+#endif\n }\n \n void Socket::bindAddress(const InetAddress& addr)\ndiff --git a/muduo/net/SocketsOps.cc b/muduo/net/SocketsOps.cc\nindex 188c3cb..1e5f268 100644\n--- a/muduo/net/SocketsOps.cc\n+++ b/muduo/net/SocketsOps.cc\n@@ -17,18 +17,26 @@\n #include <stdio.h>  // snprintf\n #include <strings.h>  // bzero\n #include <sys/socket.h>\n+#ifdef __MACH__\n+#include <sys/uio.h>  // readv\n+#endif\n #include <unistd.h>\n \n using namespace muduo;\n using namespace muduo::net;\n \n-namespace\n+namespace muduo\n {\n \n typedef struct sockaddr SA;\n \n \n #if VALGRIND || defined (NO_ACCEPT4)\n+namespace net\n+{\n+namespace sockets\n+{\n+\n void setNonBlockAndCloseOnExec(int sockfd)\n {\n   // non-block\n@@ -45,6 +53,9 @@ void setNonBlockAndCloseOnExec(int sockfd)\n \n   (void)ret;\n }\n+\n+}\n+}\n #endif\n \n }\n@@ -71,7 +82,6 @@ struct sockaddr_in* sockets::sockaddr_in_cast(struct sockaddr* addr)\n \n int sockets::createNonblockingOrDie()\n {\n-#if VALGRIND\n   int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n   if (sockfd < 0)\n   {\n@@ -79,13 +89,6 @@ int sockets::createNonblockingOrDie()\n   }\n \n   setNonBlockAndCloseOnExec(sockfd);\n-#else\n-  int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);\n-  if (sockfd < 0)\n-  {\n-    LOG_SYSFATAL << \"sockets::createNonblockingOrDie\";\n-  }\n-#endif\n   return sockfd;\n }\n \n@@ -110,13 +113,8 @@ void sockets::listenOrDie(int sockfd)\n int sockets::accept(int sockfd, struct sockaddr_in* addr)\n {\n   socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);\n-#if VALGRIND || defined (NO_ACCEPT4)\n   int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);\n   setNonBlockAndCloseOnExec(connfd);\n-#else\n-  int connfd = ::accept4(sockfd, sockaddr_cast(addr),\n-                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);\n-#endif\n   if (connfd < 0)\n   {\n     int savedErrno = errno;\ndiff --git a/muduo/net/SocketsOps.h b/muduo/net/SocketsOps.h\nindex a07b1fe..efc779a 100644\n--- a/muduo/net/SocketsOps.h\n+++ b/muduo/net/SocketsOps.h\n@@ -24,6 +24,9 @@ namespace sockets\n /// Creates a non-blocking socket file descriptor,\n /// abort if any error.\n int createNonblockingOrDie();\n+#ifdef __MACH__\n+void setNonBlockAndCloseOnExec(int sockfd);\n+#endif\n \n int  connect(int sockfd, const struct sockaddr_in& addr);\n void bindOrDie(int sockfd, const struct sockaddr_in& addr);\ndiff --git a/muduo/net/TimerQueue.cc b/muduo/net/TimerQueue.cc\nindex 0f199e5..7f4813a 100644\n--- a/muduo/net/TimerQueue.cc\n+++ b/muduo/net/TimerQueue.cc\n@@ -19,8 +19,6 @@\n \n #include <boost/bind.hpp>\n \n-#include <sys/timerfd.h>\n-\n namespace muduo\n {\n namespace net\n@@ -28,57 +26,15 @@ namespace net\n namespace detail\n {\n \n-int createTimerfd()\n-{\n-  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,\n-                                 TFD_NONBLOCK | TFD_CLOEXEC);\n-  if (timerfd < 0)\n-  {\n-    LOG_SYSFATAL << \"Failed in timerfd_create\";\n-  }\n-  return timerfd;\n-}\n-\n-struct timespec howMuchTimeFromNow(Timestamp when)\n+int howMuchTimeFromNow(Timestamp when)\n {\n   int64_t microseconds = when.microSecondsSinceEpoch()\n                          - Timestamp::now().microSecondsSinceEpoch();\n-  if (microseconds < 100)\n-  {\n-    microseconds = 100;\n-  }\n-  struct timespec ts;\n-  ts.tv_sec = static_cast<time_t>(\n-      microseconds / Timestamp::kMicroSecondsPerSecond);\n-  ts.tv_nsec = static_cast<long>(\n-      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);\n-  return ts;\n-}\n-\n-void readTimerfd(int timerfd, Timestamp now)\n-{\n-  uint64_t howmany;\n-  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);\n-  LOG_TRACE << \"TimerQueue::handleRead() \" << howmany << \" at \" << now.toString();\n-  if (n != sizeof howmany)\n-  {\n-    LOG_ERROR << \"TimerQueue::handleRead() reads \" << n << \" bytes instead of 8\";\n-  }\n-}\n-\n-void resetTimerfd(int timerfd, Timestamp expiration)\n-{\n-  // wake up loop by timerfd_settime()\n-  struct itimerspec newValue;\n-  struct itimerspec oldValue;\n-  bzero(&newValue, sizeof newValue);\n-  bzero(&oldValue, sizeof oldValue);\n-  newValue.it_value = howMuchTimeFromNow(expiration);\n-  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);\n-  if (ret)\n+  if (microseconds < 1000)\n   {\n-    LOG_SYSERR << \"timerfd_settime()\";\n+    microseconds = 1000;\n   }\n+  return static_cast<int>(microseconds / 1000);\n }\n \n }\n@@ -91,22 +47,13 @@ using namespace muduo::net::detail;\n \n TimerQueue::TimerQueue(EventLoop* loop)\n   : loop_(loop),\n-    timerfd_(createTimerfd()),\n-    timerfdChannel_(loop, timerfd_),\n     timers_(),\n     callingExpiredTimers_(false)\n {\n-  timerfdChannel_.setReadCallback(\n-      boost::bind(&TimerQueue::handleRead, this));\n-  // we are always reading the timerfd, we disarm it with timerfd_settime.\n-  timerfdChannel_.enableReading();\n }\n \n TimerQueue::~TimerQueue()\n {\n-  timerfdChannel_.disableAll();\n-  timerfdChannel_.remove();\n-  ::close(timerfd_);\n   // do not remove channel, since we're in EventLoop::dtor();\n   for (TimerList::iterator it = timers_.begin();\n       it != timers_.end(); ++it)\n@@ -146,11 +93,19 @@ void TimerQueue::cancel(TimerId timerId)\n void TimerQueue::addTimerInLoop(Timer* timer)\n {\n   loop_->assertInLoopThread();\n-  bool earliestChanged = insert(timer);\n+  insert(timer);\n+}\n \n-  if (earliestChanged)\n+int TimerQueue::getTimeout() const\n+{\n+  loop_->assertInLoopThread();\n+  if (timers_.empty())\n+  {\n+    return 10000;\n+  }\n+  else\n   {\n-    resetTimerfd(timerfd_, timer->expiration());\n+    return howMuchTimeFromNow(timers_.begin()->second->expiration());\n   }\n }\n \n@@ -174,11 +129,10 @@ void TimerQueue::cancelInLoop(TimerId timerId)\n   assert(timers_.size() == activeTimers_.size());\n }\n \n-void TimerQueue::handleRead()\n+void TimerQueue::processTimers()\n {\n   loop_->assertInLoopThread();\n   Timestamp now(Timestamp::now());\n-  readTimerfd(timerfd_, now);\n \n   std::vector<Entry> expired = getExpired(now);\n \n@@ -242,11 +196,6 @@ void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)\n   {\n     nextExpire = timers_.begin()->second->expiration();\n   }\n-\n-  if (nextExpire.valid())\n-  {\n-    resetTimerfd(timerfd_, nextExpire);\n-  }\n }\n \n bool TimerQueue::insert(Timer* timer)\ndiff --git a/muduo/net/TimerQueue.h b/muduo/net/TimerQueue.h\nindex 0cfb02f..d882b71 100644\n--- a/muduo/net/TimerQueue.h\n+++ b/muduo/net/TimerQueue.h\n@@ -56,6 +56,9 @@ class TimerQueue : boost::noncopyable\n \n   void cancel(TimerId timerId);\n \n+  int getTimeout() const;\n+  void processTimers();\n+\n  private:\n \n   // FIXME: use unique_ptr<Timer> instead of raw pointers.\n@@ -66,8 +69,6 @@ class TimerQueue : boost::noncopyable\n \n   void addTimerInLoop(Timer* timer);\n   void cancelInLoop(TimerId timerId);\n-  // called when timerfd alarms\n-  void handleRead();\n   // move out all expired timers\n   std::vector<Entry> getExpired(Timestamp now);\n   void reset(const std::vector<Entry>& expired, Timestamp now);\n@@ -75,9 +76,6 @@ class TimerQueue : boost::noncopyable\n   bool insert(Timer* timer);\n \n   EventLoop* loop_;\n-  const int timerfd_;\n-  Channel timerfdChannel_;\n-  // Timer list sorted by expiration\n   TimerList timers_;\n \n   // for cancel()\ndiff --git a/muduo/net/poller/DefaultPoller.cc b/muduo/net/poller/DefaultPoller.cc\nindex f42f5a4..a6a3133 100644\n--- a/muduo/net/poller/DefaultPoller.cc\n+++ b/muduo/net/poller/DefaultPoller.cc\n@@ -16,6 +16,9 @@ using namespace muduo::net;\n \n Poller* Poller::newDefaultPoller(EventLoop* loop)\n {\n+#ifdef __MACH__\n+  return new PollPoller(loop);\n+#else\n   if (::getenv(\"MUDUO_USE_POLL\"))\n   {\n     return new PollPoller(loop);\n@@ -24,4 +27,5 @@ Poller* Poller::newDefaultPoller(EventLoop* loop)\n   {\n     return new EPollPoller(loop);\n   }\n+#endif\n }\n\ndiff --git a/examples/protobuf/rpcbalancer/balancer_raw.cc b/examples/protobuf/rpcbalancer/balancer_raw.cc\nindex 9c2e1db..c30b19d 100644\n--- a/examples/protobuf/rpcbalancer/balancer_raw.cc\n+++ b/examples/protobuf/rpcbalancer/balancer_raw.cc\n@@ -12,7 +12,7 @@\n #include <boost/bind.hpp>\n #include <boost/ptr_container/ptr_vector.hpp>\n\n-#include <endian.h>\n+#include <machine/endian.h>\n #include <stdio.h>\n\n using namespace muduo;\n"
  },
  {
    "path": "patches/armlinux.diff",
    "content": "diff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 2c8880a..af0d174 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -21,7 +21,7 @@ set(CXX_FLAGS\n  -Wpointer-arith\n  -Wshadow\n  -Wwrite-strings\n- -march=native\n+ -march=armv4\n  # -MMD\n  # -std=c++0x\n  -rdynamic\n@@ -31,7 +31,7 @@ if(CMAKE_BUILD_BITS EQUAL 32)\n endif()\n string(REPLACE \";\" \" \" CMAKE_CXX_FLAGS \"${CXX_FLAGS}\")\n \n-set(CMAKE_CXX_COMPILER \"g++\")\n+set(CMAKE_CXX_COMPILER \"arm-g++\")\n #set(CMAKE_CXX_COMPILER \"icpc\")\n set(CMAKE_CXX_FLAGS_DEBUG \"-O0\")\n set(CMAKE_CXX_FLAGS_RELEASE \"-O2 -finline-limit=1000 -DNDEBUG\")\ndiff --git a/muduo/base/Atomic.h b/muduo/base/Atomic.h\nindex 3478da0..cc1dd45 100644\n--- a/muduo/base/Atomic.h\n+++ b/muduo/base/Atomic.h\n@@ -8,6 +8,7 @@\n \n #include <boost/noncopyable.hpp>\n #include <stdint.h>\n+#include <muduo/base/Mutex.h>\n \n namespace muduo\n {\n@@ -83,10 +84,88 @@ class AtomicIntegerT : boost::noncopyable\n  private:\n   volatile T value_;\n };\n+\n+template<typename T>\n+class AtomicIntegerLock : boost::noncopyable\n+{\n+ public:\n+  AtomicIntegerLock()\n+    : value_(0)\n+  {\n+  }\n+\n+  // uncomment if you need copying and assignment\n+  //\n+  // AtomicIntegerT(const AtomicIntegerT& that)\n+  //   : value_(that.get())\n+  // {}\n+  //\n+  // AtomicIntegerT& operator=(const AtomicIntegerT& that)\n+  // {\n+  //   getAndSet(that.get());\n+  //   return *this;\n+  // }\n+\n+  T get()\n+  {\n+    MutexLockGuard lock(mutex_);\n+    return value_;\n+  }\n+\n+  T getAndAdd(T x)\n+  {\n+    MutexLockGuard lock(mutex_);\n+    T old = value_;\n+    value_ += x;\n+    return old;\n+  }\n+\n+  T addAndGet(T x)\n+  {\n+    return getAndAdd(x) + x;\n+  }\n+\n+  T incrementAndGet()\n+  {\n+    return addAndGet(1);\n+  }\n+\n+  T decrementAndGet()\n+  {\n+    return addAndGet(-1);\n+  }\n+\n+  void add(T x)\n+  {\n+    getAndAdd(x);\n+  }\n+\n+  void increment()\n+  {\n+    incrementAndGet();\n+  }\n+\n+  void decrement()\n+  {\n+    decrementAndGet();\n+  }\n+\n+  T getAndSet(T newValue)\n+  {\n+    MutexLockGuard lock(mutex_);\n+    T old = value_;\n+    value_ = newValue;\n+    return old;\n+  }\n+\n+ private:\n+  volatile T value_;\n+  MutexLock mutex_;\n+};\n }\n \n typedef detail::AtomicIntegerT<int32_t> AtomicInt32;\n-typedef detail::AtomicIntegerT<int64_t> AtomicInt64;\n+typedef detail::AtomicIntegerLock<int64_t> AtomicInt64;\n }\n \n #endif  // MUDUO_BASE_ATOMIC_H\ndiff --git a/muduo/base/tests/CMakeLists.txt b/muduo/base/tests/CMakeLists.txt\nindex 2c3f1c4..73d90fd 100644\n--- a/muduo/base/tests/CMakeLists.txt\n+++ b/muduo/base/tests/CMakeLists.txt\n@@ -2,7 +2,7 @@\n target_link_libraries(asynclogging_test muduo_base)\n \n add_executable(atomic_unittest Atomic_unittest.cc)\n-# target_link_libraries(atomic_unittest muduo_base)\n+target_link_libraries(atomic_unittest muduo_base)\n \n add_executable(blockingqueue_test BlockingQueue_test.cc)\n target_link_libraries(blockingqueue_test muduo_base)\n"
  },
  {
    "path": "patches/backport.diff",
    "content": "diff --git a/CMakeLists.txt b/CMakeLists.txt\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -9,19 +9,19 @@ endif()\n set(CXX_FLAGS\n  -g\n  # -DVALGRIND\n- # -DMUDUO_STD_STRING\n+ -DMUDUO_STD_STRING\n  -D_FILE_OFFSET_BITS=64\n  -Wall\n  -Wextra\n  -Werror\n  -Wconversion\n  -Wno-unused-parameter\n- -Wold-style-cast\n+ # -Wold-style-cast\n  -Woverloaded-virtual\n  -Wpointer-arith\n- -Wshadow\n+ # -Wshadow\n  -Wwrite-strings\n- -march=native\n+ -march=nocona\n  # -MMD\n  # -std=c++0x\n  -rdynamic\n@@ -31,7 +31,7 @@ if(CMAKE_BUILD_BITS EQUAL 32)\n endif()\n string(REPLACE \";\" \" \" CMAKE_CXX_FLAGS \"${CXX_FLAGS}\")\n \n-set(CMAKE_CXX_COMPILER \"g++\")\n+set(CMAKE_CXX_COMPILER \"g++4\")\n #set(CMAKE_CXX_COMPILER \"icpc\")\n set(CMAKE_CXX_FLAGS_DEBUG \"-O0\")\n set(CMAKE_CXX_FLAGS_RELEASE \"-O2 -finline-limit=1000 -DNDEBUG\")\ndiff --git a/examples/idleconnection/CMakeLists.txt b/examples/idleconnection/CMakeLists.txt\n--- a/examples/idleconnection/CMakeLists.txt\n+++ b/examples/idleconnection/CMakeLists.txt\n@@ -1,4 +1,4 @@\n-add_executable(idleconnection_echo echo.cc main.cc)\n+add_executable(idleconnection_echo EXCLUDE_FROM_ALL echo.cc main.cc)\n target_link_libraries(idleconnection_echo muduo_net)\n \n add_executable(idleconnection_echo2 sortedlist.cc)\ndiff --git a/muduo/base/Timestamp.cc b/muduo/base/Timestamp.cc\n--- a/muduo/base/Timestamp.cc\n+++ b/muduo/base/Timestamp.cc\n@@ -4,7 +4,7 @@\n #include <stdio.h>\n #define __STDC_FORMAT_MACROS\n #include <inttypes.h>\n-#undef __STDC_FORMAT_MACROS\n+//#undef __STDC_FORMAT_MACROS\n \n #include <boost/static_assert.hpp>\n \ndiff --git a/muduo/base/tests/CMakeLists.txt b/muduo/base/tests/CMakeLists.txt\n--- a/muduo/base/tests/CMakeLists.txt\n+++ b/muduo/base/tests/CMakeLists.txt\n@@ -10,7 +10,7 @@ target_link_libraries(blockingqueue_test muduo_base)\n add_executable(blockingqueue_bench BlockingQueue_bench.cc)\n target_link_libraries(blockingqueue_bench muduo_base)\n \n-add_executable(boundedblockingqueue_test BoundedBlockingQueue_test.cc)\n+add_executable(boundedblockingqueue_test EXCLUDE_FROM_ALL BoundedBlockingQueue_test.cc)\n target_link_libraries(boundedblockingqueue_test muduo_base)\n \n add_executable(date_unittest Date_unittest.cc)\ndiff --git a/muduo/net/Channel.cc b/muduo/net/Channel.cc\n--- a/muduo/net/Channel.cc\n+++ b/muduo/net/Channel.cc\n@@ -21,6 +21,10 @@ const int Channel::kNoneEvent = 0;\n const int Channel::kReadEvent = POLLIN | POLLPRI;\n const int Channel::kWriteEvent = POLLOUT;\n \n+#ifndef POLLRDHUP\n+#define POLLRDHUP  0x2000\n+#endif\n+\n Channel::Channel(EventLoop* loop, int fd__)\n   : loop_(loop),\n     fd_(fd__),\ndiff --git a/muduo/net/Endian.h b/muduo/net/Endian.h\n--- a/muduo/net/Endian.h\n+++ b/muduo/net/Endian.h\n@@ -13,6 +13,7 @@\n \n #include <stdint.h>\n #include <endian.h>\n+#include <byteswap.h>\n \n namespace muduo\n {\n@@ -23,48 +24,38 @@ namespace sockets\n \n // the inline assembler code makes type blur,\n // so we disable warnings for a while.\n-#if __GNUC_MINOR__ >= 6\n-#pragma GCC diagnostic push\n-#endif\n-#pragma GCC diagnostic ignored \"-Wconversion\"\n-#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n+#if __BYTE_ORDER == __LITTLE_ENDIAN\n inline uint64_t hostToNetwork64(uint64_t host64)\n {\n-  return htobe64(host64);\n+  return bswap_64(host64);\n }\n \n inline uint32_t hostToNetwork32(uint32_t host32)\n {\n-  return htobe32(host32);\n+  return bswap_32(host32);\n }\n \n inline uint16_t hostToNetwork16(uint16_t host16)\n {\n-  return htobe16(host16);\n+  return bswap_16(host16);\n }\n \n inline uint64_t networkToHost64(uint64_t net64)\n {\n-  return be64toh(net64);\n+  return bswap_64(net64);\n }\n \n inline uint32_t networkToHost32(uint32_t net32)\n {\n-  return be32toh(net32);\n+  return bswap_32(net32);\n }\n \n inline uint16_t networkToHost16(uint16_t net16)\n {\n-  return be16toh(net16);\n+  return bswap_16(net16);\n }\n-#if __GNUC_MINOR__ >= 6\n-#pragma GCC diagnostic pop\n-#else\n-#pragma GCC diagnostic error \"-Wconversion\"\n-#pragma GCC diagnostic error \"-Wold-style-cast\"\n #endif\n \n-\n }\n }\n }\ndiff --git a/muduo/net/EventLoop.cc b/muduo/net/EventLoop.cc\n--- a/muduo/net/EventLoop.cc\n+++ b/muduo/net/EventLoop.cc\n@@ -18,7 +18,8 @@\n #include <boost/bind.hpp>\n \n #include <signal.h>\n-#include <sys/eventfd.h>\n+#include <sys/types.h>\n+#include <sys/socket.h>\n \n using namespace muduo;\n using namespace muduo::net;\n@@ -29,18 +30,6 @@ __thread EventLoop* t_loopInThisThread = 0;\n \n const int kPollTimeMs = 10000;\n \n-int createEventfd()\n-{\n-  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\n-  if (evtfd < 0)\n-  {\n-    LOG_SYSERR << \"Failed in eventfd\";\n-    abort();\n-  }\n-  return evtfd;\n-}\n-\n-#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n class IgnoreSigPipe\n {\n  public:\n@@ -50,7 +39,6 @@ class IgnoreSigPipe\n     LOG_TRACE << \"Ignore SIGPIPE\";\n   }\n };\n-#pragma GCC diagnostic error \"-Wold-style-cast\"\n \n IgnoreSigPipe initObj;\n }\n@@ -69,11 +57,17 @@ EventLoop::EventLoop()\n     threadId_(CurrentThread::tid()),\n     poller_(Poller::newDefaultPoller(this)),\n     timerQueue_(new TimerQueue(this)),\n-    wakeupFd_(createEventfd()),\n-    wakeupChannel_(new Channel(this, wakeupFd_)),\n     currentActiveChannel_(NULL)\n {\n   LOG_DEBUG << \"EventLoop created \" << this << \" in thread \" << threadId_;\n+  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, wakeupFd_) < 0)\n+  {\n+    LOG_SYSFATAL << \"Failed in socketpair\";\n+  }\n+  sockets::setNonBlockAndCloseOnExec(wakeupFd_[0]);\n+  sockets::setNonBlockAndCloseOnExec(wakeupFd_[1]);\n+  wakeupChannel_.reset(new Channel(this, wakeupFd_[0]));\n+\n   if (t_loopInThisThread)\n   {\n     LOG_FATAL << \"Another EventLoop \" << t_loopInThisThread\n@@ -93,7 +87,8 @@ EventLoop::~EventLoop()\n {\n   LOG_DEBUG << \"EventLoop \" << this << \" of thread \" << threadId_\n             << \" destructs in thread \" << CurrentThread::tid();\n-  ::close(wakeupFd_);\n+  ::close(wakeupFd_[0]);\n+  ::close(wakeupFd_[1]);\n   t_loopInThisThread = NULL;\n }\n \n@@ -108,12 +103,13 @@ void EventLoop::loop()\n   while (!quit_)\n   {\n     activeChannels_.clear();\n-    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);\n+    pollReturnTime_ = poller_->poll(timerQueue_->getTimeout(), &activeChannels_);\n     ++iteration_;\n     if (Logger::logLevel() <= Logger::TRACE)\n     {\n       printActiveChannels();\n     }\n+    timerQueue_->processTimers();\n     // TODO sort channel by priority\n     eventHandling_ = true;\n     for (ChannelList::iterator it = activeChannels_.begin();\n@@ -215,22 +211,19 @@ void EventLoop::abortNotInLoopThread()\n \n void EventLoop::wakeup()\n {\n-  uint64_t one = 1;\n-  ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);\n+  char one = '1';\n+  ssize_t n = sockets::write(wakeupFd_[1], &one, sizeof one);\n   if (n != sizeof one)\n   {\n-    LOG_ERROR << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 8\";\n+    LOG_ERROR << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 1\";\n   }\n }\n \n void EventLoop::handleRead()\n {\n-  uint64_t one = 1;\n-  ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);\n-  if (n != sizeof one)\n-  {\n-    LOG_ERROR << \"EventLoop::handleRead() reads \" << n << \" bytes instead of 8\";\n-  }\n+  char buf[4096] = { 0 };\n+  ssize_t n = sockets::read(wakeupFd_[0], buf, sizeof buf);\n+  (void)n;\n }\n \n void EventLoop::doPendingFunctors()\ndiff --git a/muduo/net/EventLoop.h b/muduo/net/EventLoop.h\n--- a/muduo/net/EventLoop.h\n+++ b/muduo/net/EventLoop.h\n@@ -130,7 +130,7 @@ class EventLoop : boost::noncopyable\n   Timestamp pollReturnTime_;\n   boost::scoped_ptr<Poller> poller_;\n   boost::scoped_ptr<TimerQueue> timerQueue_;\n-  int wakeupFd_;\n+  int wakeupFd_[2];\n   // unlike in TimerQueue, which is an internal class,\n   // we don't expose Channel to client.\n   boost::scoped_ptr<Channel> wakeupChannel_;\ndiff --git a/muduo/net/InetAddress.cc b/muduo/net/InetAddress.cc\n--- a/muduo/net/InetAddress.cc\n+++ b/muduo/net/InetAddress.cc\n@@ -17,9 +17,7 @@\n #include <boost/static_assert.hpp>\n \n // INADDR_ANY use (type)value casting.\n-#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n static const in_addr_t kInaddrAny = INADDR_ANY;\n-#pragma GCC diagnostic error \"-Wold-style-cast\"\n \n //     /* Structure describing an Internet socket address.  */\n //     struct sockaddr_in {\ndiff --git a/muduo/net/SocketsOps.cc b/muduo/net/SocketsOps.cc\n--- a/muduo/net/SocketsOps.cc\n+++ b/muduo/net/SocketsOps.cc\n@@ -37,7 +37,9 @@ SA* sockaddr_cast(struct sockaddr_in* addr)\n   return static_cast<SA*>(implicit_cast<void*>(addr));\n }\n \n-void setNonBlockAndCloseOnExec(int sockfd)\n+}\n+\n+void sockets::setNonBlockAndCloseOnExec(int sockfd)\n {\n   // non-block\n   int flags = ::fcntl(sockfd, F_GETFL, 0);\n@@ -54,12 +56,9 @@ void setNonBlockAndCloseOnExec(int sockfd)\n   (void)ret;\n }\n \n-}\n-\n int sockets::createNonblockingOrDie()\n {\n   // socket\n-#if VALGRIND\n   int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n   if (sockfd < 0)\n   {\n@@ -67,13 +66,6 @@ int sockets::createNonblockingOrDie()\n   }\n \n   setNonBlockAndCloseOnExec(sockfd);\n-#else\n-  int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);\n-  if (sockfd < 0)\n-  {\n-    LOG_SYSFATAL << \"sockets::createNonblockingOrDie\";\n-  }\n-#endif\n   return sockfd;\n }\n \n@@ -98,13 +90,8 @@ void sockets::listenOrDie(int sockfd)\n int sockets::accept(int sockfd, struct sockaddr_in* addr)\n {\n   socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);\n-#if VALGRIND\n   int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);\n   setNonBlockAndCloseOnExec(connfd);\n-#else\n-  int connfd = ::accept4(sockfd, sockaddr_cast(addr),\n-                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);\n-#endif\n   if (connfd < 0)\n   {\n     int savedErrno = errno;\ndiff --git a/muduo/net/SocketsOps.h b/muduo/net/SocketsOps.h\n--- a/muduo/net/SocketsOps.h\n+++ b/muduo/net/SocketsOps.h\n@@ -25,6 +25,8 @@ namespace sockets\n /// abort if any error.\n int createNonblockingOrDie();\n \n+void setNonBlockAndCloseOnExec(int sockfd);\n+\n int  connect(int sockfd, const struct sockaddr_in& addr);\n void bindOrDie(int sockfd, const struct sockaddr_in& addr);\n void listenOrDie(int sockfd);\ndiff --git a/muduo/net/TimerQueue.cc b/muduo/net/TimerQueue.cc\n--- a/muduo/net/TimerQueue.cc\n+++ b/muduo/net/TimerQueue.cc\n@@ -16,8 +16,6 @@\n \n #include <boost/bind.hpp>\n \n-#include <sys/timerfd.h>\n-\n namespace muduo\n {\n namespace net\n@@ -25,57 +23,15 @@ namespace net\n namespace detail\n {\n \n-int createTimerfd()\n-{\n-  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,\n-                                 TFD_NONBLOCK | TFD_CLOEXEC);\n-  if (timerfd < 0)\n-  {\n-    LOG_SYSFATAL << \"Failed in timerfd_create\";\n-  }\n-  return timerfd;\n-}\n-\n-struct timespec howMuchTimeFromNow(Timestamp when)\n+int howMuchTimeFromNow(Timestamp when)\n {\n   int64_t microseconds = when.microSecondsSinceEpoch()\n                          - Timestamp::now().microSecondsSinceEpoch();\n-  if (microseconds < 100)\n-  {\n-    microseconds = 100;\n-  }\n-  struct timespec ts;\n-  ts.tv_sec = static_cast<time_t>(\n-      microseconds / Timestamp::kMicroSecondsPerSecond);\n-  ts.tv_nsec = static_cast<long>(\n-      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);\n-  return ts;\n-}\n-\n-void readTimerfd(int timerfd, Timestamp now)\n-{\n-  uint64_t howmany;\n-  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);\n-  LOG_TRACE << \"TimerQueue::handleRead() \" << howmany << \" at \" << now.toString();\n-  if (n != sizeof howmany)\n-  {\n-    LOG_ERROR << \"TimerQueue::handleRead() reads \" << n << \" bytes instead of 8\";\n-  }\n-}\n-\n-void resetTimerfd(int timerfd, Timestamp expiration)\n-{\n-  // wake up loop by timerfd_settime()\n-  struct itimerspec newValue;\n-  struct itimerspec oldValue;\n-  bzero(&newValue, sizeof newValue);\n-  bzero(&oldValue, sizeof oldValue);\n-  newValue.it_value = howMuchTimeFromNow(expiration);\n-  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);\n-  if (ret)\n+  if (microseconds < 1000)\n   {\n-    LOG_SYSERR << \"timerfd_settime()\";\n+    microseconds = 1000;\n   }\n+  return static_cast<int>(microseconds / 1000);\n }\n \n }\n@@ -88,20 +44,13 @@ using namespace muduo::net::detail;\n \n TimerQueue::TimerQueue(EventLoop* loop)\n   : loop_(loop),\n-    timerfd_(createTimerfd()),\n-    timerfdChannel_(loop, timerfd_),\n     timers_(),\n     callingExpiredTimers_(false)\n {\n-  timerfdChannel_.setReadCallback(\n-      boost::bind(&TimerQueue::handleRead, this));\n-  // we are always reading the timerfd, we disarm it with timerfd_settime.\n-  timerfdChannel_.enableReading();\n }\n \n TimerQueue::~TimerQueue()\n {\n-  ::close(timerfd_);\n   // do not remove channel, since we're in EventLoop::dtor();\n   for (TimerList::iterator it = timers_.begin();\n       it != timers_.end(); ++it)\n@@ -129,11 +78,19 @@ void TimerQueue::cancel(TimerId timerId)\n void TimerQueue::addTimerInLoop(Timer* timer)\n {\n   loop_->assertInLoopThread();\n-  bool earliestChanged = insert(timer);\n+  insert(timer);\n+}\n \n-  if (earliestChanged)\n+int TimerQueue::getTimeout() const\n+{\n+  loop_->assertInLoopThread();\n+  if (timers_.empty())\n+  {\n+    return 10000;\n+  }\n+  else\n   {\n-    resetTimerfd(timerfd_, timer->expiration());\n+    return howMuchTimeFromNow(timers_.begin()->second->expiration());\n   }\n }\n \n@@ -157,11 +114,10 @@ void TimerQueue::cancelInLoop(TimerId timerId)\n   assert(timers_.size() == activeTimers_.size());\n }\n \n-void TimerQueue::handleRead()\n+void TimerQueue::processTimers()\n {\n   loop_->assertInLoopThread();\n   Timestamp now(Timestamp::now());\n-  readTimerfd(timerfd_, now);\n \n   std::vector<Entry> expired = getExpired(now);\n \n@@ -225,11 +181,6 @@ void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)\n   {\n     nextExpire = timers_.begin()->second->expiration();\n   }\n-\n-  if (nextExpire.valid())\n-  {\n-    resetTimerfd(timerfd_, nextExpire);\n-  }\n }\n \n bool TimerQueue::insert(Timer* timer)\ndiff --git a/muduo/net/TimerQueue.h b/muduo/net/TimerQueue.h\n--- a/muduo/net/TimerQueue.h\n+++ b/muduo/net/TimerQueue.h\n@@ -51,6 +51,9 @@ class TimerQueue : boost::noncopyable\n \n   void cancel(TimerId timerId);\n \n+  int getTimeout() const;\n+  void processTimers();\n+\n  private:\n \n   // FIXME: use unique_ptr<Timer> instead of raw pointers.\n@@ -61,8 +64,6 @@ class TimerQueue : boost::noncopyable\n \n   void addTimerInLoop(Timer* timer);\n   void cancelInLoop(TimerId timerId);\n-  // called when timerfd alarms\n-  void handleRead();\n   // move out all expired timers\n   std::vector<Entry> getExpired(Timestamp now);\n   void reset(const std::vector<Entry>& expired, Timestamp now);\n@@ -70,9 +71,6 @@ class TimerQueue : boost::noncopyable\n   bool insert(Timer* timer);\n \n   EventLoop* loop_;\n-  const int timerfd_;\n-  Channel timerfdChannel_;\n-  // Timer list sorted by expiration\n   TimerList timers_;\n \n   // for cancel()\ndiff --git a/muduo/net/poller/EPollPoller.cc b/muduo/net/poller/EPollPoller.cc\n--- a/muduo/net/poller/EPollPoller.cc\n+++ b/muduo/net/poller/EPollPoller.cc\n@@ -26,7 +26,6 @@ using namespace muduo::net;\n BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);\n BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);\n BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);\n-BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);\n BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);\n BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);\n \n@@ -39,7 +38,7 @@ const int kDeleted = 2;\n \n EPollPoller::EPollPoller(EventLoop* loop)\n   : Poller(loop),\n-    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),\n+    epollfd_(::epoll_create(32)),\n     events_(kInitEventListSize)\n {\n   if (epollfd_ < 0)\n"
  }
]