-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathen.search-data.min.c69300eb9f7b06992cf382778c2023a4140742de1b9002d7e08cd91d030390bb.json
1 lines (1 loc) · 263 KB
/
en.search-data.min.c69300eb9f7b06992cf382778c2023a4140742de1b9002d7e08cd91d030390bb.json
1
[{"id":0,"href":"/en/co/concurrency/coroutine/basic/","title":"Basic concepts","section":"Coroutine","content":"#Basic concepts of coroutine Coroutines are lightweight scheduling units that run in threads. Coroutines are to threads, similar to threads to processes. There can be multiple threads in a process and multiple coroutines in a thread. The thread where the coroutine runs in is generally called the scheduling thread. The scheduling thread will suspend a coroutine, if it blocks on an I/O operation or sleep was called in the coroutine. When a coroutine is suspended, the scheduling thread will switch to other coroutines waiting to be executed. Switching of coroutines is done in user mode, which is faster than switching between threads. Coroutines are very suitable for network programming, and can achieve synchronous programming without asynchronous callbacks, which greatly reduces the programmer\u0026rsquo;s mental burden.\nCoost implements a go-style coroutine with the following features:\nSupport multi-thread scheduling, the default number of threads is the number of system CPU cores. Coroutines in the same thread share several stacks (the default size is 1MB), and the memory usage is low. Test on Linux shows that 10 millions of coroutines only take 2.8G of memory (for reference only). Once a coroutine is created, it always runs in the same thread. There is a flat relationship between coroutines, and new coroutines can be created from anywhere (including in coroutines). The relevant code for context switching is taken from tbox by ruki, and tbox refers to the implementation of boost, thanks here!\n"},{"id":1,"href":"/en/co/def/","title":"Basic Definitions","section":"Documents","content":"include: co/def.h.\n#typedefs #Fixed-length integer type co/def.h defines the following 8 types of integers:\ntypedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; These types have the same length on different platforms, and there is no portability problem. Google Code Style recommends not to use built-in integer types such as short, long, long long, etc.\n#macros #Maximum and minimum values of integer types MAX_UINT8 MAX_UINT16 MAX_UINT32 MAX_UINT64 MAX_INT8 MAX_INT16 MAX_INT32 MAX_INT64 MIN_INT8 MIN_INT16 MIN_INT32 MIN_INT64 These macros respectively defines the maximum and minimum values of the 8 integer types.\n#DISALLOW_COPY_AND_ASSIGN This macro is used to disable copy constructor and assignment operations in C++ classes.\nExample class T { public: T(); DISALLOW_COPY_AND_ASSIGN(T); }; #__arch64, __arch32 __arch64 is defined as 1 on 64 bit platforms, __arch32 is defined as 1 on 32 bit platforms.\nExample #if __arch64 inline size_t murmur_hash(const void* s, size_t n) { return murmur_hash64(s, n, 0); } #else inline size_t murmur_hash(const void* s, size_t n) { return murmur_hash32(s, n, 0); } #endif #__forceinline __forceinline is a keyword in VS. Linux and mac platforms use the following macro simulation:\n#define __forceinline __attribute__((always_inline)) #__thread __thread is a keyword in gcc/clang to support TLS, the windows platform uses the following macro simulation:\n#define __thread __declspec(thread) Example // get id of the current thread __forceinline unsigned int gettid() { static __thread unsigned int id = 0; if (id != 0) return id; return id = __gettid(); } #unlikely This macro is used for branch prediction optimization. It only supports gcc/clang.\nExample // It is logically equivalent to if (v == 0) if (unlikey(v == 0)) { cout \u0026lt;\u0026lt; \u0026#34;v == 0\u0026#34; \u0026lt;\u0026lt; endl; } "},{"id":2,"href":"/en/co/net/byte_order/","title":"Byte order","section":"Network Programming","content":"include: co/byte_order.h.\nData in the computer is stored in bytes (8 bit). Big-endian computers use big-endian byte order, that is, the high-order byte is at the lower address, and the low-order byte is at the higher address. The little-endian machine uses little-endian byte order, that is, the low-order byte is at the lower address, and the high-order byte is at the higher address.\nA single byte is exactly the same on big endian and little endian machines, while basic data types of multiple bytes are different. The basic data types mentioned here refer to built-in types like int, double. String is not included here, as it is a sequence of single byte and have the same storage format on big or little endian machines.\nThe data transmitted on network is in big-endian byte order, which is also called network byte order. When sending data to the network, the basic multi-byte type needs to be converted into network byte order, and when receiving data from the network, it needs to be converted into the byte order of the host.\nbyte_order.h defines the following methods:\nntoh16 ntoh32 ntoh64 hton16 hton32 hton64 These methods are applicable to integers with lengths of 2, 4, and 8 bytes. The ntoh series converts network byte order to host byte order, and the hton series converts host byte order to network byte order. .\nCode example uint32 h = 777; uint32 n = hton32(h); "},{"id":3,"href":"/en/co/other/console/","title":"Console","section":"others","content":"include: co/cout.h.\n#Colored output See below:\ncout \u0026lt;\u0026lt; text::red(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::green(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::blue(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::yellow(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::magenta(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::cyan(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;hello\\n\u0026#34;; cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).red(); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).green(); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).blue(); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).yellow(); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).magenta(); cout \u0026lt;\u0026lt; text::bold(\u0026#34;hello\\n\u0026#34;).cyan(); #co::print template\u0026lt;typename ...X\u0026gt; void print(X\u0026amp;\u0026amp; ... x); Accept any number of arguments and output to stdout with a newline at the end. A mutex lock is used internally, and multiple threads can call co::print at the same time. co::print(\u0026#34;hello \u0026#34;, 23); co::print(text::red(\u0026#34;hello\u0026#34;)); co::vector\u0026lt;int\u0026gt; v = { 1, 2, 3 }; co::print(v); "},{"id":4,"href":"/en/about/co/","title":"Introduction","section":"About","content":"#What is coost coost is an elegant and efficient cross-platform C++ base library. Its goal is to create a sword of C++ to make C++ programming easy and enjoyable.\nCoost, co for short, is like boost, but more lightweight, the static library built on linux or mac is only about 1MB in size. However, it still provides enough powerful features:\nCommand line and config file parser (flag) High performance log library (log) Unit testing framework Benchmark testing framework go-style coroutine Coroutine-based network library JSON RPC framework Atomic operation (atomic) Efficient stream (fastream) Efficient string (fastring) String utility (str) Time library (time) Thread library (thread) Timed Task Scheduler God-oriented programming Efficient JSON library Hash library Path library File utilities (fs) System operations (os) Fast memory allocator #History of coost 2013-2015, Alvin(idealvin) felt a little cumbersome when using google\u0026rsquo;s gflags, glog, gtest, etc., so he implemented the corresponding functions by himself, that is, the flag, log, unitest, etc. in today\u0026rsquo;s coost.\n2015-2018, Alvin introduced this library into actual projects for use by himself and his colleagues, which greatly improved the efficiency of C++ development. It has also been tested by industrial projects, and continuously improved and expanded new features in practice.\nIn 2019, Alvin implemented a go-style coroutine and a network programming framework based on coroutines, and then named the project co and release v1.0 on github.\n2020-2021, improve hook mechanism, coroutine synchronization mechanism, add channel, defer and other features in golang, release 2.x version. During this period, some githubers provided a lot of valuable suggestions, and helped to improve xmake, cmake building scripts and many features in coost.\n2022, add a fast memory allocator, improve overall performance, make major improvements to many components such as flag, log, JSON, RPC, fastring, fastream, and rename the project coost, release version 3.0.\n#Quick Start #Compiling It is recommended to install xmake and run the following command in the root directory of coost to build all sub-projects:\nxmake -a If you need to use http::Client, SSL or HTTPS features, you can use the following command to build:\nxmake f --with_libcurl=true --with_openssl=true xmake -a Xmake will automatically install libcurl and openssl from the network. Depending on the network, this process may be slow. xmake -a will build libco, gen, co/unitest and co/test. Users can run test programs in coost with the following commands:\nxmake r unitest xmake r flag xmake r log -cout xmake r co #Develop C++ programs with coost The simplest, you can directly include co/all.h and use all the features in coost. If you are worried about the compiling speed, you can also include only the header files that you need, such as including co/co.h, you can use co/flag, co/log and all features related to coroutines.\n#include \u0026#34;co/all.h\u0026#34; DEF_string(s, \u0026#34;nice\u0026#34;, \u0026#34;\u0026#34;); int main(int argc, char** argv) { flag::parse(argc, argv); LOG \u0026lt;\u0026lt; FLG_s; return 0; } The above is a simple example. The first line of the main function is used to parse the command-line flags and the config file. Some components in coost use co/flag to define config items. Therefore, it is generally necessary to call flag::parse() at the beginning of the main function for initialization.\nUsers can also use the macro DEF_main to define the main function:\n#include \u0026#34;co/all.h\u0026#34; DEF_string(s, \u0026#34;nice\u0026#34;, \u0026#34;\u0026#34;); DEF_main(argc, argv) { LOG \u0026lt;\u0026lt; FLG_s; return 0; } DEF_main puts code in the main function into a coroutine, and flag::parse() has been called internally, and users needn\u0026rsquo;t call it manually.\n#Performance #Memory allocator For memory allocators such as ptmalloc, jemalloc, tcmalloc and mimalloc, there is a high probability that the small memory will not be returned to the operating system after they are freed. To solve this problem, coost has designed a dedicated memory allocator (co/malloc), which will return as much released memory to the system as possible while taking into account performance, which is conducive to reducing the memory footprint of the program.\nco/test provides a simple test code, which can be built and run as follow:\nxmake b mem xmake r mem -t 4 -s -t specifies the number of threads, -s means to compare with the system memory allocator. Here are the test results on different platforms (4 threads):\nos/cpu co::alloc co::free ::malloc ::free speedup win/AMD 3.2G 7.32 6.83 86.05 105.06 11.7/15.3 mac/i7 2.4G 9.91 9.86 55.64 60.20 5.6/6.1 linux/i7 2.2G 10.80 7.51 1070.5 21.17 99.1/2.8 The data in the table is the average time, the unit is nanoseconds (ns), linux is the ubuntu system running in Windows WSL, speedup is the performance improvement multiple of coost memory allocator relative to the system memory allocator.\nIt can be seen that co::alloc is nearly 99 times faster than ::malloc on Linux. One of the reasons is that ptmalloc has a large lock competition overhead in multi-threaded environment, and co/malloc is designed to avoid the use of locks as much as possible. The allocation and release of small blocks of memory do not require locks, and even spin locks are not used when releasing across threads.\n#Log library platform glog co/log speedup win2012 HHD 1.6MB/s 180MB/s 112.5 win10 SSD 3.7MB/s 560MB/s 151.3 mac SSD 17MB/s 450MB/s 26.4 linux SSD 54MB/s 1023MB/s 18.9 The above is the write speed of co/log and glog (single thread, 1 million logs). It can be seen that co/log is nearly two orders of magnitude faster than glog.\nthreads linux co/log linux spdlog win co/log win spdlog speedup 1 0.087235 2.076172 0.117704 0.461156 23.8/3.9 2 0.183160 3.729386 0.158122 0.511769 20.3/3.2 4 0.206712 4.764238 0.316607 0.743227 23.0/2.3 8 0.302088 3.963644 0.406025 1.417387 13.1/3.5 The above is the time of printing 1 million logs with 1, 2, 4, and 8 threads, in seconds. Speedup is the performance improvement of co/log compared to spdlog on linux and windows platforms.\n#JSON library os co/json stringify co/json parse rapidjson stringify rapidjson parse speedup win 569 924 2089 2495 3.6/2.7 mac 783 1097 1289 1658 1.6/1.5 linux 468 764 1359 1070 2.9/1.4 The above is the average time of stringifying and parsing minimized twitter.json, in microseconds (us), speedup is the performance improvement of co/json compared to rapidjson.\n#Core features #God-oriented programming co/god.h provides some features based on templates.\n#include \u0026#34;co/god.h\u0026#34; void f() { god::bless_no_bugs(); god::align_up\u0026lt;8\u0026gt;(31); // -\u0026gt; 32 god::is_same\u0026lt;T, int, bool\u0026gt;(); // T is int or bool? } #flag flag is a simple and easy-to-use command line and config file parsing library. Some components in coost use it to define config items.\nEach flag(config item) has a default value, and by default, the program can run with the default config values. Users can also pass in parameters from the command line or config file, and when a config file is required, -mkconf can be used to generate it automatically.\n#include \u0026#34;co/flag.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; DEF_bool(x, false, \u0026#34;x\u0026#34;); DEF_bool(debug, false, \u0026#34;dbg\u0026#34;, d); DEF_uint32(u, 0, \u0026#34;xxx\u0026#34;); DEF_string(s, \u0026#34;\u0026#34;, \u0026#34;xx\u0026#34;); int main(int argc, char** argv) { flag::parse(argc, argv); co::print(\u0026#34;x: \u0026#34;, FLG_x); co::print(\u0026#34;y: \u0026#34;, FLG_y); co::print(\u0026#34;debug: \u0026#34;, FLG_debug); co::print(\u0026#34;u: \u0026#34;, FLG_u); co::print(FLG_s, \u0026#39;|\u0026#39;, FLG_s.size()); return 0; } In the above example, the macros start with DEF_ define 4 flags. Each flag corresponds to a global variable, whose name is FLG_ plus the flag name. The flag debug has an alias d. After building, the above code can run as follow:\n./xx # Run with default configs ./xx -x -s good # x -\u0026gt; true, s -\u0026gt; \u0026#34;good\u0026#34; ./xx -debug # debug -\u0026gt; true ./xx -xd # x -\u0026gt; true, debug -\u0026gt; true ./xx -u 8k # u -\u0026gt; 8192 ./xx -mkconf # Automatically generate a config file: xx.conf ./xx xx.conf # run with a config file ./xx -conf xx.conf # Same as above #log log is a high-performance log library, some components in coost use it to print logs.\nlog supports two types of logs: one is level log, which is divided into 5 levels: debug, info, warning, error and fatal, printing a fatal log will terminate the program; the other is topic log, logs are grouped by topic, and logs of different topics are written to different files.\n#include \u0026#34;co/log.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); TLOG(\u0026#34;xx\u0026#34;) \u0026lt;\u0026lt; \u0026#34;s\u0026#34; \u0026lt;\u0026lt; 23; // topic log DLOG \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // debug LOG \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // info WLOG \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // warning ELOG \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // error FLOG \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // fatal return 0; } co/log also provides a series of CHECK macros, which is an enhanced version of assert, and they will not be cleared in debug mode.\nvoid* p = malloc(32); CHECK(p != NULL) \u0026lt;\u0026lt; \u0026#34;malloc failed..\u0026#34;; CHECK_NE(p, NULL) \u0026lt;\u0026lt; \u0026#34;malloc failed..\u0026#34;; #unitest unitest is a simple and easy-to-use unit test framework. Many components in coost use it to write unit test code, which guarantees the stability of coost.\n#include \u0026#34;co/unitest.h\u0026#34; #include \u0026#34;co/os.h\u0026#34; namespace test { DEF_test(os) { DEF_case(homedir) { EXPECT_NE(os::homedir(), \u0026#34;\u0026#34;); } DEF_case(cpunum) { EXPECT_GT(os::cpunum(), 0); } } } // namespace test The above is a simple example. The DEF_test macro defines a test unit, which is actually a function (a method in a class). The DEF_case macro defines test cases, and each test case is actually a code block. The main function is simple as below:\n#include \u0026#34;co/unitest.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); unitest::run_tests(); return 0; } The directory unitest contains the unit test code in coost. Users can run unitest with the following commands:\nxmake r unitest # Run all test cases xmake r unitest -os # Run test cases in the os unit #Benchmark benchmark is a simple and easy-to-use benchmark testing framework.\n#include \u0026#34;co/benchmark.h\u0026#34; #include \u0026#34;co/mem.h\u0026#34; BM_group(malloc) { void* p; BM_add(::malloc)( p = ::malloc(32); ); BM_use(p); BM_add(co::alloc)( p = co::alloc(32); ); BM_use(p); } int main(int argc, char** argv) { flag::parse(argc, argv); bm::run_benchmarks(); return 0; } In the above example, BM_group defines a test group, BM_add adds two test cases that need to be compared, and BM_use prevents the compiler from optimizing out the test code.\nThe benchmark results are output as a markdown table, as shown below: #JSON In coost v3.0, Json provides fluent APIs, which is more convenient to use.\n// {\u0026#34;a\u0026#34;:23,\u0026#34;b\u0026#34;:false,\u0026#34;s\u0026#34;:\u0026#34;123\u0026#34;,\u0026#34;v\u0026#34;:[1,2,3],\u0026#34;o\u0026#34;:{\u0026#34;xx\u0026#34;:0}} Json x = { { \u0026#34;a\u0026#34;, 23 }, { \u0026#34;b\u0026#34;, false }, { \u0026#34;s\u0026#34;, \u0026#34;123\u0026#34; }, { \u0026#34;v\u0026#34;, {1,2,3} }, { \u0026#34;o\u0026#34;, { {\u0026#34;xx\u0026#34;, 0} }}, }; // equal to x Json y = Json() .add_member(\u0026#34;a\u0026#34;, 23) .add_member(\u0026#34;b\u0026#34;, false) .add_member(\u0026#34;s\u0026#34;, \u0026#34;123\u0026#34;) .add_member(\u0026#34;v\u0026#34;, Json().push_back(1).push_back(2).push_back(3)) .add_member(\u0026#34;o\u0026#34;, Json().add_member(\u0026#34;xx\u0026#34;, 0)); x.get(\u0026#34;a\u0026#34;).as_int(); // 23 x.get(\u0026#34;s\u0026#34;).as_string(); // \u0026#34;123\u0026#34; x.get(\u0026#34;s\u0026#34;).as_int(); // 123, string -\u0026gt; int x.get(\u0026#34;v\u0026#34;, 0).as_int(); // 1 x.get(\u0026#34;v\u0026#34;, 2).as_int(); // 3 x.get(\u0026#34;o\u0026#34;, \u0026#34;xx\u0026#34;).as_int(); // 0 x[\u0026#34;a\u0026#34;] == 23; // true x[\u0026#34;s\u0026#34;] == \u0026#34;123\u0026#34;; // true x.get(\u0026#34;o\u0026#34;, \u0026#34;xx\u0026#34;) != 0; // false #Coroutine coost has implemented a go-style coroutine, which has the following features:\nSupport multi-thread scheduling, the default number of threads is the number of system CPU cores. Shared stack, coroutines in the same thread share several stacks (the default size is 1MB), and the memory usage is low. There is a flat relationship between coroutines, and new coroutines can be created from anywhere (including in coroutines). Support coroutine sync event, coroutine locks, channels, and waitgroups. #include \u0026#34;co/co.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); co::wait_group wg; wg.add(2); go([wg](){ LOG \u0026lt;\u0026lt; \u0026#34;hello world\u0026#34;; wg.done(); }); go([wg](){ LOG \u0026lt;\u0026lt; \u0026#34;hello again\u0026#34;; wg.done(); }); wg.wait(); return 0; } In the above code, the coroutines created by go() will be distributed to different scheduling threads. Users can also control the scheduling of coroutines by themselves:\n// run f1 and f2 in the same scheduler auto s = co::next_sched(); s-\u0026gt;go(f1); s-\u0026gt;go(f2); // run f in all schedulers for (auto\u0026amp; s : co::scheds()) { s-\u0026gt;go(f); } #network programming Coost provides a coroutine-based network programming framework:\ncoroutineized socket API, similar in form to the system socket API, users familiar with socket programming can easily write high-performance network programs in a synchronous manner. TCP, HTTP, RPC and other high-level network programming components, compatible with IPv6, also support SSL, it is more convenient to use than socket API. RPC server\n#include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/rpc.h\u0026#34; #include \u0026#34;co/time.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); rpc::Server() .add_service(new xx::HelloWorldImpl) .start(\u0026#34;127.0.0.1\u0026#34;, 7788, \u0026#34;/xx\u0026#34;); for (;;) sleep::sec(80000); return 0; } rpc::Server also supports HTTP protocol, you may use the POST method to call the RPC service:\ncurl http://127.0.0.1:7788/xx --request POST --data \u0026#39;{\u0026#34;api\u0026#34;:\u0026#34;ping\u0026#34;}\u0026#39; Static web server\n#include \u0026#34;co/flag.h\u0026#34; #include \u0026#34;co/http.h\u0026#34; DEF_string(d, \u0026#34;.\u0026#34;, \u0026#34;root dir\u0026#34;); // docroot for the web server int main(int argc, char** argv) { flag::parse(argc, argv); so::easy(FLG_d.c_str()); // mum never have to worry again return 0; } HTTP server\nvoid cb(const http::Req\u0026amp; req, http::Res\u0026amp; res) { if (req.is_method_get()) { if (req.url() == \u0026#34;/hello\u0026#34;) { res.set_status(200); res.set_body(\u0026#34;hello world\u0026#34;); } else { res.set_status(404); } } else { res.set_status(405); // method not allowed } } // http http::Server().on_req(cb).start(\u0026#34;0.0.0.0\u0026#34;, 80); // https http::Server().on_req(cb).start( \u0026#34;0.0.0.0\u0026#34;, 443, \u0026#34;privkey.pem\u0026#34;, \u0026#34;certificate.pem\u0026#34; ); HTTP client\nvoid f() { http::Client c(\u0026#34;https://github.com\u0026#34;); c.get(\u0026#34;/\u0026#34;); LOG \u0026lt;\u0026lt; \u0026#34;response code: \u0026#34;\u0026lt;\u0026lt; c.status(); LOG \u0026lt;\u0026lt; \u0026#34;body size: \u0026#34;\u0026lt;\u0026lt; c.body().size(); LOG \u0026lt;\u0026lt; \u0026#34;Content-Length: \u0026#34;\u0026lt;\u0026lt; c.header(\u0026#34;Content-Length\u0026#34;); LOG \u0026lt;\u0026lt; c.header(); c.post(\u0026#34;/hello\u0026#34;, \u0026#34;data xxx\u0026#34;); LOG \u0026lt;\u0026lt; \u0026#34;response code: \u0026#34;\u0026lt;\u0026lt; c.status(); } go(f); "},{"id":5,"href":"/en/co/concurrency/coroutine/api/","title":"APIs","section":"Coroutine","content":"#Coroutine APIs Coost v3.0 removed co::init, co::exit, co::stop. #go 1. void go(Closure* cb); 2. template\u0026lt;typename F\u0026gt; void go(F\u0026amp;\u0026amp; f); 3. template\u0026lt;typename F, typename P\u0026gt; void go(F\u0026amp;\u0026amp; f, P\u0026amp;\u0026amp; p); 4. template\u0026lt;typename F, typename T, typename P\u0026gt; void go(F\u0026amp;\u0026amp; f, T* t, P\u0026amp;\u0026amp; p); This function is used to create a coroutine, similar to creating a thread, a coroutine function must be specified.\n1, the parameter cb points to a Closure object. When the coroutine is started, the run() method of Closure will be called.\n2-4, pack the incoming parameters into a Closure, and then call the first version.\n2, the parameter f is any runnable object, as long as we can call f() or (*f)().\n3, the parameter f is any runnable object, as long as we can call f(p), (*f)(p) or (p-\u0026gt;*f)().\n4, the parameter f is a method with one parameter in the class T, the parameter t is a pointer to class T, and p is the parameter of f.\nCreating object of std::function is expensive and should be used as little as possible.\nExample\ngo(f); // void f(); go(f, 7); // void f(int); go(\u0026amp;T::f, \u0026amp;o); // void T::f(); T o; go(\u0026amp;T::f, \u0026amp;o, 3); // void T::f(int); T o; // lambda go([](){ LOG \u0026lt;\u0026lt; \u0026#34;hello co\u0026#34;; }); // std::function std::function\u0026lt;void()\u0026gt; x(std::bind(f, 7)); go(x); go(\u0026amp;x); // Ensure that x is alive when the coroutine is running. #DEF_main This macro is used to define the main function and make code in the main function also run in coroutine. DEF_main has already called flag::parse(argc, argv) to parse the command-line arguments, and users do not need to call it again.\nExample DEF_main(argc, argv) { go([](){ LOG \u0026lt;\u0026lt; \u0026#34;hello world\u0026#34;; }); co::sleep(100); return 0; } #——————————— #co::coroutine void* coroutine(); Get the current coroutine pointer. If it is not called in coroutine, the return value is NULL. The return value of this function can be passed to co::resume() to wake up the coroutine. #co::resume void resume(void* p); Wake up the specified coroutine, the parameter p is the return value of co::coroutine(). It is thread-safe and can be called anywhere. #co::yield void yield(); Suspend the current coroutine, must be called in coroutine. It can be used with co::coroutine() and co::resume() to manually control the scheduling of coroutines. See test/yield.cc for details. #——————————— #co::coroutine_id int coroutine_id(); Returns the id of the current coroutine. If it is called in non-coroutine, the return value is -1. #co::main_sched MainSched* main_sched(); This function is used to turn the main thread into a scheduling thread. Users must call the loop() method of MainSched in the main thread later. #include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); co::print(\u0026#34;main thread id: \u0026#34;, co::thread_id()); auto s = co::main_sched(); for (int i = 0; i \u0026lt; 8; ++i) { go([]{ co::print(\u0026#34;thread: \u0026#34;, co::thread_id(), \u0026#34; sched: \u0026#34;, co::sched_id()); }); } s-\u0026gt;loop(); // loop forever return 0; // fake return value } #co::next_sched Sched* next_sched(); Returns a pointer points to the next scheduler.\ngo(\u0026hellip;) is actually equivalent to co::next_sched()-\u0026gt;go(...).\nExample\n// create coroutines in the same thread auto s = co::next_sched(); s-\u0026gt;go(f1); s-\u0026gt;go(f2); #co::sched Sched* sched(); Returns a pointer points to the current scheduler. If the current thread is not a scheduling thread, the return value is NULL. #co::scheds const co::vector\u0026lt;Sched*\u0026gt;\u0026amp; scheds(); Returns a reference to the scheduler list. #co::sched_id int sched_id(); Returns id of the current scheduler. This value is between 0 and co::sched_num()-1. If the current thread is not a scheduling thread, the return value is -1. #co::sched_num int sched_num(); Returns the number of schedulers.\nExample\nco::vector\u0026lt;T\u0026gt; v(co::sched_num(), 0); void f() { // get object for the current scheduler auto\u0026amp; t = v[co::sched_id()]; } go(f); #co::stop_scheds void stop_scheds(); Stop all schedulers. #Code example // print sched id and coroutine id every 3 seconds #include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; void f() { while (true) { co::print(\u0026#34;s: \u0026#34;, co::sched_id(), \u0026#34; c: \u0026#34;, co::coroutine_id()); co::sleep(3000); } } int main(int argc, char** argv) { flag::parse(argc, argv); for (int i = 0; i \u0026lt; 32; ++i) go(f); while (true) sleep::sec(1024); return 0; } #——————————— #co::sleep void sleep(uint32 ms); Let the current coroutine sleep for a while, the parameter ms is time in milliseconds. It is usually called in coroutine. Calling it in non-coroutine is equivalent to sleep::ms(ms). #co::timeout bool timeout(); Check whether the previous IO operation has timed out. When call an API like co::recv() with a timeout, we can call this function to determine whether it has timed out. This function must be called in the coroutine. "},{"id":6,"href":"/en/co/concurrency/atomic/","title":"Atomic Operations","section":"Concurrency","content":"include: co/atomic.h.\n#Memory Order Since v3.0, co has added a support for memory order. The six memory orders in co are defined as follows:\nenum memory_order_t { mo_relaxed, mo_consume, mo_acquire, mo_release, mo_acq_rel, mo_seq_cst, }; The default memory order of atomic operations in co is mo_seq_cst.\n#APIs removed since v3.0 atomic_get, use atomic_load instead. atomic_set, use atomic_store instead. atomic_reset, use atomic_store(\u0026amp;x, 0) instead. #load \u0026amp; store #atomic_load template\u0026lt;typename T\u0026gt; inline T atomic_load(const T* p, memory_order_t mo = mo_seq_cst); This function gets the value of the variable pointed to by p, T is any built-in type (including pointer type) with a length of 1, 2, 4, 8 bytes.\nmo can be any of mo_relaxed, mo_consume, mo_acquire, mo_seq_cst.\nExample\nint i = 7; int r = atomic_load(\u0026amp;i); // r = 7 int x = atomic_load(\u0026amp;i, mo_relaxed); #atomic_store template\u0026lt;typename T, typename V\u0026gt; inline void atomic_store(T* p, V v, memory_order_t mo = mo_seq_cst); This function sets the value pointed to by p to v, T is any built-in type (including pointer type) with a length of 1, 2, 4, 8 bytes, and V is any type that can be converted to type of T.\nmo can be any of mo_relaxed, mo_release, mo_seq_cst.\nExample\nint i = 7; atomic_store(\u0026amp;i, 3); // i -\u0026gt; 3 atomic_store(\u0026amp;i, 3, mo_release); #Swap operations #atomic_swap template\u0026lt;typename T, typename V\u0026gt; inline T atomic_swap(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic swap operation, T is any built-in type (including pointer type) with a length of 1, 2, 4, 8 bytes, and V is any type that can be converted to type of T.\nThis function exchanges the value pointed to by p and v, and returns the value before the exchange.\nmo can be any type.\nExample\nbool b = false; int i = 0; void* p = 0; bool x = atomic_swap(\u0026amp;b, true); // b -\u0026gt; true, x = false int r = atomic_swap(\u0026amp;i, 1); // i -\u0026gt; 1, r = 0 void* q = atomic_swap(\u0026amp;p, (void*)8); // p -\u0026gt; 8, q = 0 #atomic_cas template\u0026lt;typename T, typename O, typename V\u0026gt; inline T atomic_cas( T* p, O o, V v, memory_order_t smo = mo_seq_cst, memory_order_t fmo = mo_seq_cst ); template \u0026lt;typename T, typename O, typename V\u0026gt; inline T atomic_compare_swap( T* p, O o, V v, memory_order_t smo = mo_seq_cst, memory_order_t fmo = mo_seq_cst ); Atomic swap operation, T is any built-in type (including pointer type) with a length of 1, 2, 4, 8 bytes, O and V are any types that can be converted to type of T.\nThis function exchanges with v only when the value pointed to by p is equal to o.\nThis function returns the value before the exchange operation.\nExample\nbool b = false; int i = 0; void* p = 0; bool x = atomic_cas(\u0026amp;b, false, true); // b -\u0026gt; true, x = false int r = atomic_cas(\u0026amp;i, 1, 2); // No swap, i remains unchanged, r = 0 void* q = atomic_cas(\u0026amp;p, 0, (void*)8); // p -\u0026gt; 8, q = 0 #atomic_bool_cas template\u0026lt;typename T, typename O, typename V\u0026gt; inline bool atomic_bool_cas( T* p, O o, V v, memory_order_t smo = mo_seq_cst, memory_order_t fmo = mo_seq_cst ); Like the atomic_cas, but returns true if the swap operation was successful, otherwise returns false. #Arithmetic operations #atomic_inc template\u0026lt;typename T\u0026gt; inline T atomic_inc(T* p, memory_order_t mo = mo_seq_cst); Atomic increment, T is any integer type with a length of 1, 2, 4, 8 bytes, and the parameter p is a pointer of type T.\nThis function performs an increment operation on the integer pointed to by p and returns the result after increment.\nExample\nint i = 0; uint64 u = 0; int r = atomic_inc(\u0026amp;i); // i -\u0026gt; 1, r = 1 uint64 x = atomic_inc(\u0026amp;u); // u -\u0026gt; 1, x = 1 #atomic_fetch_inc template\u0026lt;typename T\u0026gt; inline T atomic_fetch_inc(T* p, memory_order_t mo = mo_seq_cst); The same as atomic_inc, but returns the value before increment.\nExample\nint i = 0; uint64 u = 0; int r = atomic_fetch_inc(\u0026amp;i); // i -\u0026gt; 1, r = 0 uint64 x = atomic_fetch_inc(\u0026amp;u); // u -\u0026gt; 1, x = 0 #atomic_dec template\u0026lt;typename T\u0026gt; inline T atomic_dec(T* p, memory_order_t mo = mo_seq_cst); Atomic decrement, T is any integer type with a length of 1, 2, 4, 8 bytes, and the parameter p is a pointer of type T.\nThis function decrements the integer pointed to by p and returns the decremented result.\nExample\nint i = 1; uint64 u = 1; int r = atomic_dec(\u0026amp;i); // i -\u0026gt; 0, r = 0 uint64 x = atomic_dec(\u0026amp;u); // u -\u0026gt; 0, x = 0 #atomic_fetch_dec template\u0026lt;typename T\u0026gt; inline T atomic_fetch_dec(T* p, memory_order_t mo = mo_seq_cst); The same as atomic_dec, but returns the value before decrement.\nExample\nint i = 1; uint64 u = 1; int r = atomic_fetch_dec(\u0026amp;i); // i -\u0026gt; 0, r = 1 uint64 x = atomic_fetch_dec(\u0026amp;u); // u -\u0026gt; 0, x = 1 #atomic_add template\u0026lt;typename T, typename V\u0026gt; inline T atomic_add(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic addition, T is any integer type with a length of 1, 2, 4, 8 bytes, V is any integer type, and the parameter p is a pointer of type T.\nThis function adds the value v to the integer pointed to by p, and returns the result after adding v.\nExample\nint i = 0; uint64 u = 0; int r = atomic_add(\u0026amp;i, 1); // i -\u0026gt; 1, r = 1 uint64 x = atomic_add(\u0026amp;u, 1); // u -\u0026gt; 1, x = 1 #atomic_fetch_add template\u0026lt;typename T, typename V\u0026gt; inline T atomic_fetch_add(T* p, V v, memory_order_t mo = mo_seq_cst); The same as atomic_add, but returns the value before adding v.\nExample\nint i = 0; uint64 u = 0; int r = atomic_fetch_add(\u0026amp;i, 1); // i -\u0026gt; 1, r = 0 uint64 x = atomic_fetch_add(\u0026amp;u, 1); // u -\u0026gt; 1, x = 0 #atomic_sub template\u0026lt;typename T, typename V\u0026gt; inline T atomic_sub(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic subtraction, T is any integer type with a length of 1, 2, 4, 8 bytes, V is any integer type, and the parameter p is a pointer of type T.\nThis function subtracts value v from the integer pointed to by p, and returns the result of subtracting v.\nExample\nint i = 1; uint64 u = 1; int r = atomic_sub(\u0026amp;i, 1); // i -\u0026gt; 0, r = 0 uint64 x = atomic_sub(\u0026amp;u, 1); // u -\u0026gt; 0, x = 0 #atomic_fetch_sub template\u0026lt;typename T, typename V\u0026gt; inline T atomic_fetch_sub(T* p, V v, memory_order_t mo = mo_seq_cst); The same as atomic_sub, but returns the value before subtracting v.\nExample\nint i = 1; uint64 u = 1; int r = atomic_fetch_sub(\u0026amp;i, 1); // i -\u0026gt; 0, r = 1 uint64 x = atomic_fetch_sub(\u0026amp;u, 1); // u -\u0026gt; 0, x = 1 #Bit operation #atomic_or template\u0026lt;typename T, typename V\u0026gt; inline T atomic_or(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic bitwise OR operation, T is any integer type with a length of 1, 2, 4, 8 bytes, V is any integer type, and the parameter p is a pointer of type T.\nThis function performs bitwise OR operation on the integer pointed to by p and v, and returns the result of the operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_or(\u0026amp;i, 3); // i |= 3, i -\u0026gt; 7, r = 7 uint64 x = atomic_or(\u0026amp;u, 3); // u |= 3, u -\u0026gt; 7, x = 7 #atomic_fetch_or template\u0026lt;typename T, typename V\u0026gt; inline T atomic_fetch_or(T* p, V v, memory_order_t mo = mo_seq_cst); The same as atomic_or, but returns the value before the bitwise OR operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_fetch_or(\u0026amp;i, 3); // i |= 3, i -\u0026gt; 7, r = 5 uint64 x = atomic_fetch_or(\u0026amp;u, 3); // u |= 3, u -\u0026gt; 7, x = 5 #atomic_and template\u0026lt;typename T, typename V\u0026gt; inline T atomic_and(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic bitwise AND operation, T is any integer type with a length of 1, 2, 4, 8 bytes, V is any integer type, and the parameter p is a pointer of type T.\nThis function performs bitwise AND operation on the integer pointed to by p and v, and returns the result after the operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_and(\u0026amp;i, 3); // i \u0026amp;= 3, i -\u0026gt; 1, r = 1 uint64 x = atomic_and(\u0026amp;u, 3); // u \u0026amp;= 3, u -\u0026gt; 1, x = 1 #atomic_fetch_and template\u0026lt;typename T, typename V\u0026gt; inline T atomic_fetch_and(T* p, V v, memory_order_t mo = mo_seq_cst); The same as atomic_and, but returns the value before the bitwise AND operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_fetch_and(\u0026amp;i, 3); // i \u0026amp;= 3, i -\u0026gt; 1, r = 5 uint64 x = atomic_fetch_and(\u0026amp;u, 3); // u \u0026amp;= 3, u -\u0026gt; 1, x = 5 #atomic_xor template\u0026lt;typename T, typename V\u0026gt; inline T atomic_xor(T* p, V v, memory_order_t mo = mo_seq_cst); Atomic bitwise XOR operation, T is any integer type with a length of 1, 2, 4, 8 bytes, V is any integer type, and the parameter p is a pointer of type T.\nThis function performs a bitwise XOR operation on the integer pointed to by p and v, and returns the result of the operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_xor(\u0026amp;i, 3); // i ^= 3, i -\u0026gt; 6, r = 6 uint64 x = atomic_xor(\u0026amp;u, 3); // u ^= 3, u -\u0026gt; 6, x = 6 #atomic_fetch_xor template\u0026lt;typename T, typename V\u0026gt; inline T atomic_fetch_xor(T* p, V v, memory_order_t mo = mo_seq_cst); The same as atomic_xor, but returns the value before the bitwise XOR operation.\nExample\nint i = 5; uint64 u = 5; int r = atomic_fetch_xor(\u0026amp;i, 3); // i ^= 3, i -\u0026gt; 6, r = 5 uint64 x = atomic_fetch_xor(\u0026amp;u, 3); // u ^= 3, u -\u0026gt; 6, x = 5 "},{"id":7,"href":"/en/about/contact/","title":"Contact","section":"About","content":"Contact\nEmail: idealvin at qq.com github: https://github.com/idealvin/coost gitee: https://gitee.com/idealvin/coost zhihu: idealvin "},{"id":8,"href":"/en/co/other/defer/","title":"defer","section":"others","content":"include: co/defer.h.\n#defer defer is a macro provided by coost, which is similar to defer in golang. It accepts one or more statements as the arguments.\nvoid f() { void* p = malloc(32); defer(free(p)); defer( std::cout \u0026lt;\u0026lt; \u0026#34;111\u0026#34; \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026#34;222\u0026#34; \u0026lt;\u0026lt; std::endl; ); std::cout \u0026lt;\u0026lt; \u0026#34;333\u0026#34; \u0026lt;\u0026lt; std::endl; } In the above example, the code in defer will be executed at the end of function f, so 333 is printed before 111 and 222.\n"},{"id":9,"href":"/en/co/other/error/","title":"Error","section":"others","content":"include: co/error.h.\n#co::error 1. int error(); 2. void error(int e); 1, returns the current error code. 2, set the current error code to e. #co::strerror 1. const char* strerror(int e); 2. const char* strerror(); 1, get the description information of the error code e, thread-safe. 2, get the description information of the current error code, thread-safe. "},{"id":10,"href":"/en/co/god/","title":"God Oriented Programming","section":"Documents","content":"include: co/god.h.\n#god The god module provides some features on templates. C++ code written with templates may be hard to understand. Some C++ programmers call it god-oriented programming.\n#god::bless_no_bugs void bless_no_bugs(); Pray to God to bless the code is bug-free. It is thread-safe, and can be called arbitrarily.\nexample\n#include \u0026#34;co/god.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; int main(int argc, char** argv) { god::bless_no_bugs(); co::print(\u0026#34;hello world\u0026#34;); return 0; } #god::cast template\u0026lt;typename To, typename From\u0026gt; constexpr To cast(From\u0026amp;\u0026amp; f) { return (To) std::forward\u0026lt;From\u0026gt;(f); } Universal conversion, convert From type to To type, To can be a reference.\nexample\nint i = 65; char c = god::cast\u0026lt;char\u0026gt;(i); // c -\u0026gt; \u0026#39;A\u0026#39; god::cast\u0026lt;char\u0026amp;\u0026gt;(i) = \u0026#39;a\u0026#39;; // set the lower byte of i to \u0026#39;a\u0026#39; #——————————— #god::align_down 1. template\u0026lt;size_t A, typename X\u0026gt; constexpr X align_down(X x); 2. template\u0026lt;size_t A, typename X\u0026gt; constexpr X* align_down(X* x); 3. template\u0026lt;typename X, typename A\u0026gt; constexpr X align_down(X x, A a); 4. template\u0026lt;typename X, typename A\u0026gt; constexpr X* align_down(X* x, A a); Aligns an integer or pointer down.\n1, align the integer x down by the compile-time constant A, which must be power of 2.\n2, Align pointer x down by compile-time constant A, A must be power of 2.\n3, align the integer x down by the integer a, which must be power of 2.\n4, Align pointer x down by integer a, a must be power of 2.\nexample\ngod::align_down\u0026lt;8\u0026gt;(30); // 24 god::align_down(30, 8); // 24 god::align_down\u0026lt;64\u0026gt;(30); // 0 void* p = (void*)40; god::align_down\u0026lt;64\u0026gt;(p); // (void*)0 god::align_down\u0026lt;32\u0026gt;(p); // (void*)32 god::align_down(p, 32); // (void*)32 #god::align_up 1. template\u0026lt;size_t A, typename X\u0026gt; constexpr X align_up(X x); 2. template\u0026lt;size_t A, typename X\u0026gt; constexpr X* align_up(X* x); 3. template\u0026lt;typename X, typename A\u0026gt; constexpr X align_up(X x, A a); 4. template\u0026lt;typename X, typename A\u0026gt; constexpr X* align_up(X* x, A a); Aligns an integer or pointer up.\n1, align the integer x up by the compile-time constant A, A must be power of 2.\n2, align the pointer x up by the compile-time constant A, A must be power of 2.\n3, align integer x up by integer a, a must be power of 2.\n4, align pointer x up by integer a, a must be power of 2.\nexample\ngod::align_up\u0026lt;8\u0026gt;(30); // 32 god::align_up(30, 8); // 32 god::align_up\u0026lt;64\u0026gt;(30); // 64 void* p = (void*)40; god::align_up\u0026lt;64\u0026gt;(p); // (void*)64 god::align_up\u0026lt;32\u0026gt;(p); // (void*)64 god::align_up(p, 32); // (void*)64 #god::copy template\u0026lt;size_t N\u0026gt; inline void copy(void* dst, const void* src); Copy N bytes from src to dst.\nexample\nchar s[8] = \u0026#34;1234567\u0026#34;; uint32 x; god::copy\u0026lt;4\u0026gt;(\u0026amp;x, s + 1); #god::eq template\u0026lt;typename T\u0026gt; bool eq(const void* p, const void* q); Check whether *(T*)p == *(T*)q is true.\nexample\ngod::eq\u0026lt;uint32\u0026gt;(\u0026#34;nicecode\u0026#34;, \u0026#34;nicework\u0026#34;); // true god::eq\u0026lt;uint64\u0026gt;(\u0026#34;nicecode\u0026#34;, \u0026#34;nicework\u0026#34;); // false #god::log2 template\u0026lt;typename T\u0026gt; constexpr T log2(T x); Computes the base-2 logarithm of an integer x, rounding only the integer part of the result.\nexample\ngod::log2(32); // 5 god::log2(5); // 2 #god::nb template\u0026lt;size_t N, typename X\u0026gt; constexpr X nb(X x); Calculate the number of blocks, N is the block size, N must be power of 2.\nexample\ngod::nb\u0026lt;8\u0026gt;(31); // 4 god::nb\u0026lt;16\u0026gt;(32); // 2 god::nb\u0026lt;32\u0026gt;(33); // 2 #——————————— #god::fetch_add template\u0026lt;typename T, typename V\u0026gt; inline T fetch_add(T* p, V v); Add v to the value of *p, return the original value.\nexample\nint i = 8; int x = god::fetch_add(\u0026amp;i, 1); // i -\u0026gt; 9, x -\u0026gt; 8 #god::fetch_sub template\u0026lt;typename T, typename V\u0026gt; inline T fetch_sub(T* p, V v); Subtract v from the value of *p, return the original value.\nexample\nint i = 8; int x = god::fetch_sub(\u0026amp;i, 1); // i -\u0026gt; 7, x -\u0026gt; 8 #god::fetch_and template\u0026lt;typename T, typename V\u0026gt; inline T fetch_and(T* p, V v); Perform the and operation on *p, return the original value.\nexample\nuint32 u = 5; uint32 x = god::fetch_and(\u0026amp;u, 1); // u -\u0026gt; 1, x -\u0026gt; 5 #god::fetch_or template\u0026lt;typename T, typename V\u0026gt; inline T fetch_or(T* p, V v); Perform an or operation on *p, return the original value.\nexample\nuint32 u = 2; uint32 x = god::fetch_or(\u0026amp;u, 1); // u -\u0026gt; 3, x -\u0026gt; 2 #god::fetch_xor template\u0026lt;typename T, typename V\u0026gt; inline T fetch_xor(T* p, V v); Perform the xor operation on *p, return the original value.\nexample\nuint32 u = 2; uint32 x = god::fetch_xor(\u0026amp;u, 2); // u -\u0026gt; 0, x -\u0026gt; 2 #god::swap template\u0026lt;typename T, typename V\u0026gt; inline T swap(T* p, V v); Set the value of *p to v, return the original value.\nexample\nuint32 u = 23; uint32 x = god::swap(\u0026amp;u, 77); // u -\u0026gt; 77, x -\u0026gt; 23 #——————————— #god::const_ref_t template\u0026lt;typename T\u0026gt; using _const_t = typename std::add_const\u0026lt;T\u0026gt;::type; template\u0026lt;typename T\u0026gt; using const_ref_t = typename std::add_lvalue_reference\u0026lt;_const_t\u0026lt;rm_ref_t\u0026lt;T\u0026gt;\u0026gt;\u0026gt;::type; Added const and lvalue references.\nexample\ngod::const_ref_t\u0026lt;int\u0026gt; -\u0026gt; const int\u0026amp; #god::if_t template\u0026lt;bool C, typename T=void\u0026gt; using if_t = typename std::enable_if\u0026lt;C, T\u0026gt;::type; Equivalent to std::enable_if_t. #god::rm_arr_t template\u0026lt;typename T\u0026gt; using rm_arr_t = typename std::remove_extent\u0026lt;T\u0026gt;::type; Remove the first array dimension of T.\nExample\nGod::rm_arr_t\u0026lt;int[8]\u0026gt; -\u0026gt; int god::rm_arr_t\u0026lt;int[6][8]\u0026gt; -\u0026gt; int[8] #god::rm_cv_t template\u0026lt;typename T\u0026gt; using rm_cv_t = typename std::remove_cv\u0026lt;T\u0026gt;::type; Remove const and volatile from T. #god::rm_ref_t template\u0026lt;typename T\u0026gt; using rm_ref_t = typename std::remove_reference\u0026lt;T\u0026gt;::type; Remove references in T.\nExample\ngod::rm_ref_t\u0026lt;bool\u0026amp;\u0026gt; -\u0026gt; bool god::rm_ref_t\u0026lt;int\u0026amp;\u0026amp;\u0026gt; -\u0026gt; int #god::rm_cvref_t template\u0026lt;typename T\u0026gt; using rm_cvref_t = rm_cv_t\u0026lt;rm_ref_t\u0026lt;T\u0026gt;\u0026gt;; Combination of rm_cv_t and rm_ref_t.\nExample\ngod::rm_cvref_t\u0026lt;const int\u0026amp;\u0026gt; -\u0026gt; int #——————————— #god::is_array template\u0026lt;typename T\u0026gt; constexpr bool is_array(); Determine whether T is an array.\nexample\ngod::is_array\u0026lt;char[8]\u0026gt;(); // true god::is_array\u0026lt;char*\u0026gt;(); // false #god::is_class template\u0026lt;typename T\u0026gt; constexpr bool is_class(); Determine whether T is a class.\nexample\ngod::is_class\u0026lt;std::string\u0026gt;(); // true #god::is_ref template\u0026lt;typename T\u0026gt; constexpr bool is_ref(); Determine whether T is a reference. god::is_ref\u0026lt;const int\u0026amp;\u0026gt;(); // true god::is_ref\u0026lt;int\u0026amp;\u0026amp;\u0026gt;(); // true #god::is_same template\u0026lt;typename T, typename U, typename ... X\u0026gt; constexpr bool is_same(); Determine whether T is one of U or X....\nexample\ngod::is_same\u0026lt;int, int\u0026gt;(); // true god::is_same\u0026lt;int, bool, int\u0026gt;(); // true god::is_same\u0026lt;int, bool, char\u0026gt;(); // false #god::is_scalar template\u0026lt;typename T\u0026gt; constexpr bool is_scalar(); Determine whether T is a scalar type. "},{"id":11,"href":"/en/co/net/sock/","title":"Socket programming","section":"Network Programming","content":"include: co/co.h.\n#Socket APIs Coost provides commonly used coroutineized socket APIs to support coroutine-based network programming.\nMost of the APIs are consistent in form with the native socket APIs, which can reduce the user\u0026rsquo;s learning burden, and users who are familiar with socket programming can easily get started.\nMost of these APIs need to be used in coroutines. When they are blocked on I/O or sleep, the scheduling thread will suspend the current coroutine and switch to other waiting coroutines, and the scheduling thread itself will not block. With these APIs, users can easily implement high-concurrency and high-performance network programs.\n#Terminology convention Blocking\nWhen describing some socket APIs (accept, recv, etc.) in coost, the term blocking is used. The document says that they will block, which means that the current coroutine will block, but the scheduling thread will not (it can switch to other coroutines to run). From the user\u0026rsquo;s point of view, they saw the coroutines which may block. In fact, these APIs use non-blocking socket internally, which does not really block, but when the socket is not readable or writable, the scheduling thread will suspend the current coroutine. When the socket becomes readable or writable, the scheduling thread will awaken the coroutine again and continue the I/O operation.\nnon-blocking socket\nThe socket APIs in coost must use non-blocking socket. On windows, socket must also support overlapped I/O, which is supported by default for sockets created with win32 API, users generally no need to worry about it. For narrative convenience, when non-blocking socket is mentioned in the document, it also means that it supports overlapped I/O on windows.\n#co::socket 1. sock_t socket(int domain, int type, int proto); 2. sock_t tcp_socket(int domain=AF_INET); 3. sock_t udp_socket(int domain=AF_INET); Create a socket. 1, the form is exactly the same as the native API. You can use man socket to see the details on linux. 2, create a TCP socket. 3, create a UDP socket. The parameter domain is usually AF_INET or AF_INET6, the former means ipv4 and the latter means ipv6. These functions return a non-blocking socket. When an error occurs, the return value is -1, and co::error() can be called to get the error code. #co::accept sock_t accept(sock_t fd, void* addr, int* addrlen); Receive the client connection on the specified socket, the parameter fd is a non-blocking socket, and the parameters addr and addrlen are used to receive the client\u0026rsquo;s address information. The initial value of *addrlen is the length of the buffer pointed to by addr. If the user does not need the client address information, addr and addrlen should be set to NULL. This function must be called in coroutine. This function will block until a new connection comes in or an error occurs. This function returns a non-blocking socket on success, and returns -1 when an error occurs, and co::error() can be called to get the error code. #co::bind int bind(sock_t fd, const void* addr, int addrlen); Bind the ip address to the socket, the parameters addr and addrlen are the address information, which is the same as the native API. This function returns 0 on success, otherwise returns -1, and co::error() can be called to get the error code. #co::close int close(sock_t fd, int ms=0); Close the socket. In coost v2.0.0 or before, a socket must be closed in the same thread that performed the I/O operation. Since v2.0.1, a socket can be closed anywhere. When ms \u0026gt; 0, it will call co::sleep(ms) to suspend the current coroutine for a period of time, and then close the socket. The EINTR signal has been processed internally in this function, and the user does not need to consider it. This function returns 0 on success, otherwise it returns -1, and co::error() can be called to get the error code. #co::connect int connect(sock_t fd, const void* addr, int addrlen, int ms=-1); Create a connection to the specified address on the specified socket, parameter fd must be non-blocking, ms is the timeout period in milliseconds, the default is -1, which will never time out. This function must be called in coroutine. This function will block until the connection is completed, or timeout or an error occurs. This function returns 0 on success, and returns -1 on timeout or an error occurs. The user can call co::timeout() to check whether it has timed out, and co::error() can be called to get the error code. #co::listen int listen(sock_t fd, int backlog=1024); Listenning on the specified socket. This function returns 0 on success, otherwise it returns -1, and co::error() can be called to get the error code. #co::recv int recv(sock_t fd, void* buf, int n, int ms=-1); Receive data on the specified socket, parameter fd must be non-blocking, buf is the buffer to receive the data, n is the buffer length, and ms is the timeout period in milliseconds, the default is -1, never time out. This function must be called in coroutine. On Windows, this function only works with TCP-like stream socket. This function will block until any data comes in, or timeout or any error occurs. This function returns length of the data received (may be less than n) on success, returns 0 when the peer closes the connection, returns -1 when timeout or an error occurs, and users can call co::timeout() to check whether it has timed out, and co::error() can be called to get the error code. #co::recvn int recvn(sock_t fd, void* buf, int n, int ms=-1); Receive n bytes on the specified socket, fd must be non-blocking, ms is the timeout period in milliseconds, the default is -1, never timeout. This function must be called in coroutine. This function will block until all n bytes of data are received, or timeout or an error occurs. This function returns n on success, returns 0 when the peer closes the connection, and returns -1 when timeout or an error occurs. The user can call co::timeout() to check whether it has timed out, and co::error() can be called to get the error code. #co::recvfrom int recvfrom(sock_t fd, void* buf, int n, void* src_addr, int* addrlen, int ms=-1); Similar to co::recv, except that the parameters src_addr and addrlen can be used to receive the source address information. The initial value of *addrlen is the length of the buffer pointed to by src_addr. If the user does not need the source address information, addr and addrlen should be set to NULL . It is recommended to use this function to receive UDP data only. #co::send int send(sock_t fd, const void* buf, int n, int ms=-1); Send data to the specified socket, parameter fd must be non-blocking, ms is the timeout period in milliseconds, the default is -1, which will never time out. This function must be called in coroutine. On Windows, this function only works with TCP-like stream socket. This function will block until all n bytes of data are sent, or timeout or an error occurs. This function returns n on success, and returns -1 on timeout or an error occurs. The user can call co::timeout() to check whether it has timed out, and co::error() can be called to get the error code. #co::sendto int sendto(sock_t fd, const void* buf, int n, const void* dst_addr, int addrlen, int ms=-1); Send data to the specified address. When dst_addr is NULL and addrlen is 0, it is equivalent to co::send. It is recommended to use this function to send UDP data only. When fd is a UDP socket, the maximum of n is 65507. #co::shutdown int shutdown(sock_t fd, char c=\u0026#39;b\u0026#39;); This function is generally used to half-close the socket. The parameter c is a hint, 'r' for read, 'w' for write, the default is 'b' for both read and write. It is better to call this function in the same thread that performed the I/O operation. This function returns 0 on success, otherwise it returns -1, and co::error() can be called to get the error code. #——————————— #co::getsockopt int getsockopt(sock_t fd, int lv, int opt, void* optval, int* optlen); Get socket option information, which is exactly the same as native API, man getsockopt for details. #co::setsockopt int setsockopt(sock_t fd, int lv, int opt, const void* optval, int optlen); Set the socket option information, which is exactly the same as the native API, man setsockopt for details. #co::set_nonblock void set_nonblock(sock_t fd); Set O_NONBLOCK option for the socket. #co::set_reuseaddr void set_reuseaddr(sock_t fd); Set SO_REUSEADDR option for the socket. Generally, the listening socket of a server needs to set this option, or bind may fail when the server restarts. #co::set_recv_buffer_size void set_recv_buffer_size(sock_t fd, int n); Set receiving buffer size of the socket. This function must be called before the socket is connected. #co::set_send_buffer_size void set_send_buffer_size(sock_t fd, int n); Set sending buffer size of the socket. This function must be called before the socket is connected. #co::set_tcp_keepalive void set_tcp_keepalive(sock_t fd); Set SO_KEEPALIVE option for the socket. #co::set_tcp_nodelay void set_tcp_nodelay(sock_t fd); Set TCP_NODELAY option for the socket. #co::reset_tcp_socket int reset_tcp_socket(sock_t fd, int ms=0); Reset a TCP connection, similar to co::close, but the caller will not enter the TIME_WAIT state. Generally, only the server side will call this function to close a client connection without entering the TIME_WAIT state. #——————————— #co::addr2str 1. fastring addr2str(const struct sockaddr_in* addr); 2. fastring addr2str(const struct sockaddr_in6* addr); 3. fastring addr2str(const void* addr, int len); Convert the socket address into a string in the form of \u0026quot;ip:port\u0026quot;. 1, for ipv4 addresses, 2 for ipv6 addresses. The third one calls version 1 or version 2 according to addrlen. #co::init_addr 1. bool init_addr(struct sockaddr_in* addr, const char* ip, int port); 2. bool init_addr(struct sockaddr_in6* addr, const char* ip, int port); Initialize the sockaddr structure with ip and port.\n1, for ipv4 addresses, 2 for ipv6 addresses.\nExample\nunion { struct sockaddr_in v4; struct sockaddr_in6 v6; } addr; co::init_ip_addr(\u0026amp;addr.v4, \u0026#34;127.0.0.1\u0026#34;, 7777); co::init_ip_addr(\u0026amp;addr.v6, \u0026#34;::\u0026#34;, 7777); #co::init_ip_addr bool init_ip_addr(struct sockaddr_in* addr, const char* ip, int port); bool init_ip_addr(struct sockaddr_in6* addr, const char* ip, int port); Deprecated since v3.0.1, use co::init_addr instead. #co::peer fastring peer(sock_t fd); Get the address information of the peer. The return value is a string in the form of \u0026quot;ip:port\u0026quot;. "},{"id":12,"href":"/en/co/concurrency/thread/","title":"Thread","section":"Concurrency","content":"include: co/thread.h.\n#Thread Coost v3.0.1 removed the header file co/thread.h, and removed the global class Thread and Mutex, which are nearly the same as std::thread and std::mutex in C++11. Users can use the std version directly. #co::thread_id uint32 thread_id(); Returns the id value of the current thread. #co::sync_event Coost v3.0.1 removed the global SyncEvent, please use co::sync_event instead. #constructor explicit sync_event(bool manual_reset=false, bool signaled=false); Constructor, parameter manual_reset indicates whether to manually reset the event to unsynced state, parameter signaled indicates whether the initial state is synced. #sync_event::reset void reset(); Reset the event to unsynced state. When manual_reset is true in the constructor, users need manually call this method after calling wait() to set the event to unsynced state. #sync_event::signal void signal(); Generates a sync signal, set the event to synced state. #sync_event::wait 1. void wait(); 2. bool wait(uint32 ms); 1, wait until the event becomes synced. 2, wait until the event becomes synced or timed out. The parameter ms specifies the timeout in milliseconds. Returns true if the event becomes synced, false otherwise. When manual_reset is false in the constructor, wait() will automatically set the event to unsynced state. #Code Example #include \u0026#34;co/co.h\u0026#34; bool manual_reset = false; co::sync_event ev(manual_reset); void f1() { if (!ev.wait(1000)) { LOG \u0026lt;\u0026lt; \u0026#34;f1: timedout..\u0026#34;; } else { LOG \u0026lt;\u0026lt; \u0026#34;f1: event signaled..\u0026#34;; if (manual_reset) ev.reset(); } } void f2() { LOG \u0026lt;\u0026lt; \u0026#34;f2: send a signal..\u0026#34;; ev.signal(); } int main(int argc, char** argv) { std::thread(f1).detach(); std::thread(f2).detach(); co::sleep(3000); return 0; } #co::tls template\u0026lt;typename T\u0026gt; class tls; co::tls wraps the system\u0026rsquo;s thread local interface.\nthread_ptr was removed in v3.0.1, and co::tls was provided instead. #constructor tls(); The constructor, allocate system resources and initialize. #tls::get T* get() const; Return the pointer value owned by the current thread. #tls::set void set(T* p); Set the pointer value owned by the current thread. #tls::operator-\u0026gt; T* operator-\u0026gt;() const; Returns the pointer value owned by the current thread. #tls::operator* T\u0026amp; operator*() const; Returns a reference to the object pointed to by the pointer owned by the current thread. #operator== bool operator==(T* p) const; Check whether the pointer owned by the current thread is equal to p. #operator!= bool operator!=(T* p) const; Check whether the pointer owned by the current thread is not equal to p. #operator bool explicit operator bool() const; Returns true if the pointer owned by the current thread is not NULL, false otherwise. "},{"id":13,"href":"/en/co/concurrency/coroutine/chan/","title":"Channel","section":"Coroutine","content":"include: co/co.h.\n#co::chan template\u0026lt;typename T\u0026gt; class chan; co::chan is a template class, which is similar to the channel in golang and is used to pass data between coroutines.\nSince v3.0.1, co::chan can be used in coroutines or non-coroutines, and can store values of non-POD types such as std::string. #constructor 1. explicit chan(uint32 cap=1, uint32 ms=(uint32)-1); 2. chan(chan\u0026amp;\u0026amp; c); 3. chan(const chan\u0026amp; c); 1, cap is the maximum capacity of the internal queue, the default is 1. Parameter ms is the timeout for read and write operations in milliseconds, the default is -1 and never timed out. 2, the move constructor. 3, the copy constructor, only increases the internal reference count by 1. #close void close() const; Close the channel. After closing, the channel cannot be written, but can be read. #done bool done() const; Check whether the read or write operation is successfully completed. #operator bool explicit operator bool() const; If the channel is closed, returns false, otherwise returns true. #operator\u0026laquo; chan\u0026amp; operator\u0026lt;\u0026lt;(const T\u0026amp; x) const; chan\u0026amp; operator\u0026lt;\u0026lt;(T\u0026amp;\u0026amp; x) const; Write element x. This method blocks until the write operation completes or times out. You can call the done() method to determine whether the write operation is completed successfully. If the channel is closed, the write operation will fail and this method will return immediately. #operator\u0026raquo; chan\u0026amp; operator\u0026gt;\u0026gt;(T\u0026amp; x) const; Read an element from the channel. This method blocks until the read operation completes or times out. You can call the done() method to determine whether the read operation is completed successfully. If the channel is closed and there is no element in the channel, the read operation will fail and this method will return immediately. #Code Example #include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; void f() { co::chan\u0026lt;int\u0026gt; ch; go([ch]() { ch \u0026lt;\u0026lt; 7; }); int v = 0; ch \u0026gt;\u0026gt; v; co::print(\u0026#34;v: \u0026#34;, v); } void g() { co::chan\u0026lt;int\u0026gt; ch(32, 500); go([ch]() { ch \u0026lt;\u0026lt; 7; if (!ch.done()) co::print(\u0026#34;write to channel timeout..\u0026#34;); }); int v = 0; ch \u0026gt;\u0026gt; v; if (ch.done()) co::print(\u0026#34;v: \u0026#34;, v); } int main(int argc, char** argv) { flag::parse(argc, argv); f(); g(); return 0; } "},{"id":14,"href":"/en/co/other/fastring/","title":"fastring","section":"others","content":"include: co/fastring.h.\n#fastring fastring is the string type in coost. It implements most of the methods in std::string and also provides some methods that std::string does not support.\n#constructor 1. constexpr fastring() noexcept; 2. explicit fastring(size_t cap); 3. fastring(const void* s, size_t n); 4. fastring(const char* s); 5. fastring(const std::string\u0026amp; s); 6. fastring(size_t n, char c); 7. fastring(const fastring\u0026amp; s); 8. fastring(fastring\u0026amp;\u0026amp; s) noexcept; 1, the default constructor, which creates an empty fastring without allocating any memory. 2, creates an empty fastring, but uses the parameter cap to specify the initial capacity. 3, creates a fastring from the given byte sequence, and the parameter n is the sequence length. 4, creates a fastring from a null-terminated string. 5, creates a fastring from std::string. 6, initialize fastring to a string of n characters c. 7, the copy constructor, which will copy the internal memory. 8, the move constructor, which does not copy the memory. coost v3.0.2 removed fastring(char c, size_t n);. Example fastring s; // empty string, no memory allocation fastring s(32); // empty string, pre-allocated memory (capacity is 32) fastring s(\u0026#34;hello\u0026#34;); // initialize s to \u0026#34;hello\u0026#34; fastring s(\u0026#34;hello\u0026#34;, 3); // initialize s to \u0026#34;hel\u0026#34; fastring s(88,\u0026#39;x\u0026#39;); // initialize s to 88 \u0026#39;x\u0026#39; fastring t(s); // copy construction fastring x(std::move(s)); // Move construction, s itself becomes empty #operator= 1. fastring\u0026amp; operator=(const char* s); 2. fastring\u0026amp; operator=(const std::string\u0026amp; s); 3. fastring\u0026amp; operator=(const fastring\u0026amp; s); 4. fastring\u0026amp; operator=(fastring\u0026amp;\u0026amp; s) noexcept; 1, assigns a null-terminated string to fastring, s can be part of the fastring for assignment.\n2, assigns the value of std::string to fastring.\n3, the copy assignment operation.\n4, the move assignment operation, s itself will become empty.\nExample\nfastring s; fastring t; s = \u0026#34;hello\u0026#34;; // s -\u0026gt; \u0026#34;hello\u0026#34; s = s; // nothing will be done s = s.c_str() + 2; // s -\u0026gt; \u0026#34;llo\u0026#34; s = std::string(\u0026#34;x\u0026#34;); // s -\u0026gt; \u0026#34;x\u0026#34; t = s; // t -\u0026gt; \u0026#34;x\u0026#34; t = std::move(s); // t -\u0026gt; \u0026#34;x\u0026#34;, s -\u0026gt; \u0026#34;\u0026#34; #assign 1. fastring\u0026amp; assign(const void* s, size_t n); 2. template\u0026lt;typename S\u0026gt; fastring\u0026amp; assign(S\u0026amp;\u0026amp; s); 3. fastring\u0026amp; assign(size_t n, char c); Assign value to fastring, added in v3.0.1. 1, n is the length of s, s can be part of the fastring itself. 2, equivalent to operator=. 3, added in v3.0.2, assign n characters c to fastring. #——————————— #back char\u0026amp; back(); const char\u0026amp; back() const; This method returns a reference to the last character in fastring. If fastring is empty, calling this method will cause undefined behavior. Example fastring s(\u0026#34;hello\u0026#34;); char c = s.back(); // c =\u0026#39;o\u0026#39; s.back() =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;hellx\u0026#34; #front char\u0026amp; front(); const char\u0026amp; front() const; This method returns the reference of the first character in fastring. If fastring is empty, calling this method will cause undefined behavior. Example fastring s(\u0026#34;hello\u0026#34;); char c = s.front(); // c =\u0026#39;h\u0026#39; s.front() =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;xello\u0026#34; #operator[] char\u0026amp; operator[](size_t n); const char\u0026amp; operator[](size_t n) const; This method returns the reference of the nth character in fastring. If `n`` is out of a reasonable range, calling this method will cause undefined behavior. Example fastring s(\u0026#34;hello\u0026#34;); char c = s[1]; // c =\u0026#39;e\u0026#39; s[1] =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;hxllo\u0026#34; #——————————— #capacity size_t capacity() const noexcept; This method returns the capacity of fastring. #c_str const char* c_str() const; This method gets the equivalent C-style string (null terminated). Writing to the character array accessed through c_str() is undefined behavior. #data char* data() noexcept; const char* data() const noexcept; This method is similar to c_str(), but it does not guarantee that the string ends with \u0026lsquo;\\0\u0026rsquo;. #empty bool empty() const noexcept; This method determines whether fastring is empty. #size size_t size() const noexcept; This method returns the length of fastring. #——————————— #clear 1. void clear(); 2. void clear(char c); Set the size of fastring to 0, the capacity remains unchanged. The second is similar to the first, except that it will fill the memory with character c before setting its size to 0. The second one is added in v3.0.1, which can be used to clear sensitive information in memory. #ensure void ensure(size_t n); This method ensures that the remaining memory of fastring can hold at least n characters. #reserve void reserve(size_t n); This method ensures that the capacity of fastring is at least n. When n is less than the original capacity, the capacity remains unchanged. #reset void reset(); Added in v2.0.3. Clear fastring and free the memory. #resize 1. void resize(size_t n); 2. void resize(size_t n, char c); This method sets the size of fastream to n.\nWhen n is greater than the original size, it will expand size to n. In the 1st version, the content of the extended part is undefined. The 2nd version will fill the extended part with character c.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.resize(3); // s -\u0026gt; \u0026#34;hel\u0026#34; s.resize(6); char c = s[5]; // c is an uncertain random value s.resize(3); s.resize(6, 0); c = s[5]; // c is \u0026#39;\\0\u0026#39; #shrink void shrink(); Release the extra memory in fastring.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.reserve(32); // capacity -\u0026gt; 32 s.shrink(); // capacity -\u0026gt; 6 #swap void swap(fastring\u0026amp; s) noexcept; void swap(fastring\u0026amp;\u0026amp; s) noexcept; Swap the contents of two fastrings, only the data pointer, capacity, and size are exchanged internally.\nExample\nfastring s(\u0026#34;hello\u0026#34;); fastring x(\u0026#34;world\u0026#34;); s.swap(x); // s -\u0026gt; \u0026#34;world\u0026#34;, x -\u0026gt; \u0026#34;hello\u0026#34; #——————————— #append 1. fastring\u0026amp; append(const void* s, size_t n); 2. fastring\u0026amp; append(const char* s); 3. fastring\u0026amp; append(const fastring\u0026amp; s); 4. fastring\u0026amp; append(const std::string\u0026amp; s); 5. fastring\u0026amp; append(size_t n, char c); 6. fastring\u0026amp; append(char c); 1, appends a byte sequence of length n. 2, appends a null-terminated string, and s can be part of fastring that performs the append operation. 3, appends a fastring, and s can be the fastring itself that performs the append operation. 4, appends std::string. 5, appends n characters c. 6, appends a single character c. v3.0.2 removed fastring\u0026amp; append(char c, size_t n);. Example fastring s; s.append(\u0026#39;c\u0026#39;); // s -\u0026gt; \u0026#34;c\u0026#34; s.append(2, \u0026#39;c\u0026#39;); // s -\u0026gt; \u0026#34;ccc\u0026#34; s.clear(); s.append(\u0026#39;c\u0026#39;).append(2, \u0026#39;x\u0026#39;); // s -\u0026gt; \u0026#34;cxx\u0026#34; s.append(s.c_str() + 1); // s -\u0026gt; \u0026#34;cxxxx\u0026#34; s.append(s.data(), 3); // s -\u0026gt; \u0026#34;cxxxxcxx\u0026#34; #append_nomchk fastring\u0026amp; append_nomchk(const void* s, size_t n); fastring\u0026amp; append_nomchk(const char* s) Similar to append(), but will not check if s overlaps with the internal memory. This method cannot be used if s may overlap with the internal memory of fastring. #cat template\u0026lt;typename X, typename ...V\u0026gt; fastring\u0026amp; cat(X\u0026amp;\u0026amp; x, V\u0026amp;\u0026amp; ... v); Added in v2.0.3. Concatenate any number of elements to fastring.\nThis method appends elements in the parameters to fastring one by one through operator\u0026lt;\u0026lt;.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.cat(\u0026#39; \u0026#39;, 23, \u0026#34;xx\u0026#34;, false); // s -\u0026gt; \u0026#34;hello 23xxfalse\u0026#34; #operator\u0026laquo; fastring\u0026amp; operator\u0026lt;\u0026lt;(bool v); fastring\u0026amp; operator\u0026lt;\u0026lt;(char v); fastring\u0026amp; operator\u0026lt;\u0026lt;(signed char v); fastring\u0026amp; operator\u0026lt;\u0026lt;(unsigned char v); fastring\u0026amp; operator\u0026lt;\u0026lt;(short v); fastring\u0026amp; operator\u0026lt;\u0026lt;(unsigned short v); fastring\u0026amp; operator\u0026lt;\u0026lt;(int v); fastring\u0026amp; operator\u0026lt;\u0026lt;(unsigned int v); fastring\u0026amp; operator\u0026lt;\u0026lt;(long v); fastring\u0026amp; operator\u0026lt;\u0026lt;(unsigned long v); fastring\u0026amp; operator\u0026lt;\u0026lt;(long long v); fastring\u0026amp; operator\u0026lt;\u0026lt;(unsigned long long v); fastring\u0026amp; operator\u0026lt;\u0026lt;(double v); fastring\u0026amp; operator\u0026lt;\u0026lt;(float v); fastring\u0026amp; operator\u0026lt;\u0026lt;(const dp::_fpt\u0026amp; v); fastring\u0026amp; operator\u0026lt;\u0026lt;(const void* v); fastring\u0026amp; operator\u0026lt;\u0026lt;(std::nullptr_t); fastring\u0026amp; operator\u0026lt;\u0026lt;(const char* s); fastring\u0026amp; operator\u0026lt;\u0026lt;(const signed char* s); fastring\u0026amp; operator\u0026lt;\u0026lt;(const unsigned char* s); fastring\u0026amp; operator\u0026lt;\u0026lt;(const fastring\u0026amp; s); fastring\u0026amp; operator\u0026lt;\u0026lt;(const std::string\u0026amp; s); Format value of bool, char, integer type, floating point type, pointer type or string type, and append it to fastring.\noperator\u0026lt;\u0026lt;(const dp::_fpt\u0026amp; v) is used to output formatted floating-point number, which the effective decimal places can be specified.\nExample\nfastring s; s \u0026lt;\u0026lt; \u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;x\u0026#34; s \u0026lt;\u0026lt; s; // s -\u0026gt; \u0026#34;xx\u0026#34; (append itself) s \u0026lt;\u0026lt; false; // s -\u0026gt; \u0026#34;xxfalse\u0026#34; s.clear(); s \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // s -\u0026gt; \u0026#34;hello 23\u0026#34; s \u0026lt;\u0026lt; (s.c_str() + 6); // s -\u0026gt; \u0026#34;hello 2323\u0026#34; (append part of s) s.clear(); s \u0026lt;\u0026lt; 3.1415; // s -\u0026gt; \u0026#34;3.1415\u0026#34; s.clear(); s \u0026lt;\u0026lt; (void*)32; // s -\u0026gt; \u0026#34;0x20\u0026#34; Specify the number of valid decimal places\ncoost provides dp::_1, dp::_2, ..., dp::_16, dp::_n to set the number of effective decimal places of floating-point numbers.\nfastring s; s \u0026lt;\u0026lt; dp::_2(3.1415); // \u0026#34;3.14 s \u0026lt;\u0026lt; dp::_3(3.1415); // \u0026#34;3.141\u0026#34; s \u0026lt;\u0026lt; dp::_n(3.14, 1); // \u0026#34;3.1\u0026#34;, equivalent to dp::_1(3.14) #operator+= fastring\u0026amp; operator+=(const char* s); fastring\u0026amp; operator+=(const fastring\u0026amp; s); fastring\u0026amp; operator+=(const std::string\u0026amp; s); fastring\u0026amp; operator+=(char c); This method is equivalent to the append() method.\nExample\nfastring s; s += \u0026#39;c\u0026#39;; // s -\u0026gt; \u0026#34;c\u0026#34; s += \u0026#34;xx\u0026#34;; // s -\u0026gt; \u0026#34;cxx\u0026#34; #push_back fastring\u0026amp; push_back(char c); Append single character to fastring, equivalent to append(c). #pop_back char pop_back(); Pop and return the last character of fastring. If fastring is empty, calling this method will cause undefined behavior. #——————————— #compare 1. int compare(const char* s, size_t n) const; 2. int compare(const char* s) const; 3. int compare(const fastring\u0026amp; s) const noexcept; 4. int compare(const std::string\u0026amp; s) const noexcept; 5. int compare(size_t pos, size_t len, const char* s, size_t n) const; 6. int compare(size_t pos, size_t len, const char* s) const; 7. int compare(size_t pos, size_t len, const fastring\u0026amp; s) const; 8. int compare(size_t pos, size_t len, const std::string\u0026amp; s) const; 9. int compare(size_t pos, size_t len, const fastring\u0026amp; s, size_t spos, size_t n=npos) const; 10. int compare(size_t pos, size_t len, const std::string\u0026amp; s, size_t spos, size_t n=npos) const; Compare fastring with the specified string. 1-4, compare fastring with s, n is the length of s. 5-8, Compare the [pos, pos+len) part of fastring with s, n is the length of s. 9-10, Compare the [pos, pos+len) part of fastring with the [spos, spos+n) part of s. #contains bool contains(char c) const; bool contains(const char* s) const; bool contains(const fastring\u0026amp; s) const; bool contains(const std::string\u0026amp; s) const; Check if the fastring contains the given character or substring. #find 1. size_t find(char c) const; 2. size_t find(char c, size_t pos) const; 3. size_t find(char c, size_t pos, size_t len) const; 4. size_t find(const char* s) const; 5. size_t find(const char* s, size_t pos) const; 6. size_t find(const char* s, size_t pos, size_t n) const; 7. size_t find(const fastring\u0026amp; s, size_t pos=0) const; 8. size_t find(const std::string\u0026amp; s, size_t pos=0) const; 1, search for character c from position 0. 2, search for character c from position pos. 3, added in v3.0, search for character c in range [pos, pos + len). 4, search substring s from position 0. 5, search substring s from position pos. 6, search substring s from position pos, n is the length of s. 7-8, search substring s from position pos. This method returns the position of the character or substring when the search succeeds, otherwise it returns npos. Coost v3.0.1 adds the above 6-8, and supports the search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.find(\u0026#39;l\u0026#39;); // return 2 s.find(\u0026#39;l\u0026#39;, 3); // return 3 s.find(\u0026#34;ll\u0026#34;); // return 2 s.find(\u0026#34;ll\u0026#34;, 3); // return s.npos #ifind size_t ifind(char c, size_t pos=0) const; size_t ifind(const char* s) const; size_t ifind(const char* s, size_t pos) const size_t ifind(const char* s, size_t pos, size_t n) const; size_t ifind(const fastring\u0026amp; s, size_t pos=0) const; size_t ifind(const std::string\u0026amp; s, size_t pos=0) const; Added in v3.0.1, similar to find, but will ignore the case.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.ifind(\u0026#39;L\u0026#39;); // return 2 s.ifind(\u0026#39;L\u0026#39;, 3); // return 3 s.ifind(\u0026#34;Ll\u0026#34;); // return 2 #rfind 1. size_t rfind(char c) const; 2. size_t rfind(char c, size_t pos) const; 3. size_t rfind(const char* s) const; 4. size_t rfind(const char* s, size_t pos) const; 5. size_t rfind(const char* s, size_t pos, size_t n) const; 6. size_t rfind(const fastring\u0026amp; s, size_t pos=npos) const; 7. size_t rfind(const std::string\u0026amp; s, size_t pos=npos) const; Similar to find, but search in reverse order. Coost v3.0.1 adds the above 2, 4-7, and supports reverse search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.rfind(\u0026#39;l\u0026#39;); // return 3 s.rfind(\u0026#39;l\u0026#39;, 2); // return 2 s.rfind(\u0026#34;ll\u0026#34;); // return 2 s.rfind(\u0026#34;le\u0026#34;); // return s.npos #find_first_of 1. size_t find_first_of(const char* s, size_t pos=0) const; 2. size_t find_first_of(const char* s, size_t pos, size_t n) const; 3. size_t find_first_of(const fastring\u0026amp; s, size_t pos=0) const; 4. size_t find_first_of(const std::string\u0026amp; s, size_t pos=0) const; Find the first occurrence of a character in the specified character set s, n is the length of s. This method searches from position pos(0 by default), and when it encounters any character in s, it returns the position of the character, otherwise it returns npos. Coost v3.0.1 adds 2-4, and supports search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.find_first_of(\u0026#34;def\u0026#34;); // return 1 s.find_first_of(\u0026#34;ol\u0026#34;, 3); // return 3 #find_first_not_of 1. size_t find_first_not_of(char c, size_t pos=0) const; 2. size_t find_first_not_of(const char* s, size_t pos=0) const; 3. size_t find_first_not_of(const char* s, size_t pos, size_t n) const; 4. size_t find_first_not_of(const fastring\u0026amp; s, size_t pos=0) const; 5. size_t find_first_not_of(const std::string\u0026amp; s, size_t pos=0) const; Find the first occurrence of a character not equal to c or not in the specified character set s, from position pos, n is the length of s. This method searches from postion pos(0 by default), when it encounters any character not equal to c or not in s, it returns the position of the character, otherwise it returns npos. Coost v3.0.1 adds 3-5, and supports search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.find_first_not_of(\u0026#34;he\u0026#34;); // return 2 s.find_first_not_of(\u0026#34;he\u0026#34;, 3); // return 3 s.find_first_not_of(\u0026#39;x\u0026#39;); // return 0 #find_last_of 1. size_t find_last_of(const char* s, size_t pos=npos) const; 2. size_t find_last_of(const char* s, size_t pos, size_t n) const; 3. size_t find_last_of(const fastring\u0026amp; s, size_t pos=npos) const; 4. size_t find_last_of(const std::string\u0026amp; s, size_t pos=npos) const; Find the last occurrence of a character in the specified character set s. This method starts a reverse search from pos(npos by default), and when it encounters any character in s, it returns the position of the character, otherwise it returns npos. Coost v3.0.1 adds 2-4. This method supports search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.find_last_of(\u0026#34;le\u0026#34;); // return 3 s.find_last_of(\u0026#34;le\u0026#34;, 1); // return 1 #find_last_not_of 1. size_t find_last_not_of(char c, size_t pos=npos) const; 2. size_t find_last_not_of(const char* s, size_t pos=npos) const; 3. size_t find_last_not_of(const char* s, size_t pos, size_t n) const; 4. size_t find_last_not_of(const fastring\u0026amp; s, size_t pos=npos) const; 5. size_t find_last_not_of(const std::string\u0026amp; s, size_t pos=npos) const; Find the last occurrence of a character not equal to c or not in the specified character set s. This method starts a reverse search from pos(npos by default), and when it encounters any character not equal to c or not in s, it returns the position of the character, otherwise it returns npos. Coost v3.0.1 adds 3-5, and supports search of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.find_last_not_of(\u0026#34;le\u0026#34;); // return 4 s.find_last_not_of(\u0026#34;le\u0026#34;, 3); // return 0 s.find_last_not_of(\u0026#39;o\u0026#39;); // return 3 #npos static const size_t npos = (size_t)-1; It is the maximum value of the size_t type.\nWhen it is used as the length, it means until the end of the string.\nWhen it is used as the return value, it means not found.\nExample\nfastring s(\u0026#34;hello\u0026#34;); size_t r = s.find(\u0026#39;x\u0026#39;); r == s.npos; // true #substr fastring substr(size_t pos) const; fastring substr(size_t pos, size_t len) const; The first version returns the substring starting at position pos.\nThe second version returns a substring of length len starting at position pos.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.substr(2); // \u0026#34;llo\u0026#34; s.substr(2, 2); // \u0026#34;ll\u0026#34; #——————————— #starts_with bool starts_with(char c) const; bool starts_with(const char* s, size_t n) const; bool starts_with(const char* s) const; bool starts_with(const fastring\u0026amp; s) const; bool starts_with(const std::string\u0026amp; s) const; This method determines whether fastring starts with character c or string s, n is the length of s. When s is empty, this method always returns true. #ends_with bool ends_with(char s) const; bool ends_with(const char* s, size_t n) const; bool ends_with(const char* s) const; bool ends_with(const fastring\u0026amp; s) const; bool ends_with(const std::string\u0026amp; s) const; This method determines whether fastring ends with character c or string s, n is the length of s. When s is empty, this method always returns true. #match bool match(const char* pattern) const; Check whether fastring matches the pattern, * matches 0 or more characters, and ? matches exactly one character.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s.match(\u0026#34;he??o\u0026#34;); // true s.match(\u0026#34;h*o\u0026#34;); // true s.match(\u0026#34;he?o\u0026#34;); // false s.match(\u0026#34;*o\u0026#34;); // true s.match(\u0026#34;h*l?\u0026#34;); // true #remove_prefix fastring\u0026amp; remove_prefix(const char* s, size_t n); fastring\u0026amp; remove_prefix(const char* s); fastring\u0026amp; remove_prefix(const fastring\u0026amp; s); fastring\u0026amp; remove_prefix(const std::string\u0026amp; s); Remove the prefix s of fastring, n is the length of s.\nAdded in v3.0.1.\nExample\nfastring s(\u0026#34;12345678\u0026#34;); s.remove_prefix(\u0026#34;123\u0026#34;); // s -\u0026gt; \u0026#34;45678\u0026#34; s.remove_prefix(\u0026#34;xxx\u0026#34;); // nothing will be done #remove_suffix fastring\u0026amp; remove_suffix(const char* s, size_t n); fastring\u0026amp; remove_suffix(const char* s); fastring\u0026amp; remove_suffix(const fastring\u0026amp; s); fastring\u0026amp; remove_suffix(const std::string\u0026amp; s); Remove the suffix s from fastring, n is the length of s. Coost v3.0.1 renames remove_tail to remove_suffix. Example fastring s(\u0026#34;hello.log\u0026#34;); s.remove_suffix(\u0026#34;.log\u0026#34;); // s -\u0026gt; \u0026#34;hello\u0026#34; s.remove_suffix(\u0026#34;.xxx\u0026#34;); // nothing will be done #——————————— #replace 1. fastring\u0026amp; replace(const char* sub, const char* to, size_t maxreplace=0); 2. fastring\u0026amp; replace(const fastring\u0026amp; sub, const fastring\u0026amp; to, size_t maxreplace=0); 3. fastring\u0026amp; replace(const char* sub, size_t n, const char* to, size_t m, size_t maxreplace=0); This method replaces the substring sub in fastring with to. The parameter maxreplace specifies the maximum number of replacements, and 0 means unlimited. n and m are the lengths of the substrings sub and to respectively. Coost v3.0.1 adds 2-3, and supports replacement of binary strings. Example fastring s(\u0026#34;hello\u0026#34;); s.replace(\u0026#34;ll\u0026#34;, \u0026#34;rr\u0026#34;); // s -\u0026gt; \u0026#34;herro\u0026#34; s.replace(\u0026#34;err\u0026#34;, \u0026#34;ok\u0026#34;).replace(\u0026#34;k\u0026#34;, \u0026#34;o\u0026#34;); // s -\u0026gt; \u0026#34;hooo\u0026#34; #strip fastring\u0026amp; strip(const char* s=\u0026#34; \\t\\r\\n\u0026#34;, char d=\u0026#39;b\u0026#39;); fastring\u0026amp; strip(char s, char d=\u0026#39;b\u0026#39;); Trim the string, the same as trim. #trim 1. fastring\u0026amp; trim(char c, char d=\u0026#39;b\u0026#39;); 2. fastring\u0026amp; trim(unsigned char c, char d=\u0026#39;b\u0026#39;); 3. fastring\u0026amp; trim(signed char c, char d=\u0026#39;b\u0026#39;); 4. fastring\u0026amp; trim(const char* s=\u0026#34; \\t\\r\\n\u0026#34;, char d=\u0026#39;b\u0026#39;); 5. fastring\u0026amp; trim(size_t n, char d=\u0026#39;b\u0026#39;); 6. fastring\u0026amp; trim(int n, char d=\u0026#39;b\u0026#39;); Trim the string, remove the specified characters on the left, right, or both sides of fastring. The parameter d is the direction, \u0026rsquo;l\u0026rsquo; or \u0026lsquo;L\u0026rsquo; means left, \u0026lsquo;r\u0026rsquo; or \u0026lsquo;R\u0026rsquo; means right, and the default is \u0026lsquo;b\u0026rsquo; for both sides. 1-3, remove single character c from left, right or both sides. 4, remove characters in s on the left, right or both sides. 5-6, remove n characters from the left, right or both sides. Since v3.0.1, the parameter c can be \\0. Example fastring s = \u0026#34; sos\\r\\n\u0026#34;; s.trim(); // s -\u0026gt; \u0026#34;sos\u0026#34; s.trim(\u0026#39;s\u0026#39;, \u0026#39;l\u0026#39;); // s -\u0026gt; \u0026#34;os\u0026#34; s.trim(\u0026#39;s\u0026#39;, \u0026#39;r\u0026#39;); // s -\u0026gt; \u0026#34;o\u0026#34; s = \u0026#34;[[xx]]\u0026#34;; s.trim(2); // s -\u0026gt; \u0026#34;xx\u0026#34; #tolower fastring\u0026amp; tolower(); This method converts fastring to lowercase and returns a reference to fastring. #toupper fastring\u0026amp; toupper(); This method converts fastring to uppercase and returns a reference to fastring. #lower fastring lower() const; This method returns the lowercase form of fastring. #upper fastring upper() const; This method returns the uppercase form of fastring.\nExample\nfastring s(\u0026#34;Hello\u0026#34;); fastring x = s.lower(); // x = \u0026#34;hello\u0026#34;, s remains unchanged fastring y = s.upper(); // x = \u0026#34;HELLO\u0026#34;, s remains unchanged s.tolower(); // s -\u0026gt; \u0026#34;hello\u0026#34; s.toupper(); // s -\u0026gt; \u0026#34;HELLO\u0026#34; #global functions #operator+ fastring operator+(const fastring\u0026amp; a, char b); fastring operator+(char a, const fastring\u0026amp; b); fastring operator+(const fastring\u0026amp; a, const fastring\u0026amp; b); fastring operator+(const fastring\u0026amp; a, const char* b); fastring operator+(const char* a, const fastring\u0026amp; b); fastring operator+(const fastring\u0026amp; a, const std::string\u0026amp; b); fastring operator+(const std::string\u0026amp; a, const fastring\u0026amp; b); At least one parameter of this method is fastring.\nExample\nfastring s; s = s + \u0026#39;^\u0026#39;; // s -\u0026gt; \u0026#34;^\u0026#34; s = \u0026#34;o\u0026#34; + s + \u0026#34;o\u0026#34;; // s -\u0026gt; \u0026#34;o^o\u0026#34; #operator== bool operator==(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator==(const fastring\u0026amp; a, const char* b); bool operator==(const char* a, const fastring\u0026amp; b); bool operator==(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator==(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether two strings are equal, at least one parameter is fastring. #operator!= bool operator!=(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator!=(const fastring\u0026amp; a, const char* b); bool operator!=(const char* a, const fastring\u0026amp; b); bool operator!=(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator!=(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether two strings are not equal, at least one parameter is fastring. #operator\u0026lt; bool operator\u0026lt;(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator\u0026lt;(const fastring\u0026amp; a, const char* b); bool operator\u0026lt;(const char* a, const fastring\u0026amp; b); bool operator\u0026lt;(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator\u0026lt;(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether the string a is less than b, and at least one parameter is fastring. #operator\u0026gt; bool operator\u0026gt;(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator\u0026gt;(const fastring\u0026amp; a, const char* b); bool operator\u0026gt;(const char* a, const fastring\u0026amp; b); bool operator\u0026gt;(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator\u0026gt;(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether the string a is greater than b, and at least one parameter is fastring. #operator\u0026lt;= bool operator\u0026lt;=(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator\u0026lt;=(const fastring\u0026amp; a, const char* b); bool operator\u0026lt;=(const char* a, const fastring\u0026amp; b); bool operator\u0026lt;=(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator\u0026lt;=(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether the string a is less than or equal to b. At least one parameter is fastring. #operator\u0026gt;= bool operator\u0026gt;=(const fastring\u0026amp; a, const fastring\u0026amp; b); bool operator\u0026gt;=(const fastring\u0026amp; a, const char* b); bool operator\u0026gt;=(const char* a, const fastring\u0026amp; b); bool operator\u0026gt;=(const fastring\u0026amp; a, const std::string\u0026amp; b); bool operator\u0026gt;=(const std::string\u0026amp; a, const fastring\u0026amp; b); This method determines whether the string a is greater than or equal to b, and at least one parameter is fastring.\nExample\nfastring s(\u0026#34;hello\u0026#34;); s == \u0026#34;hello\u0026#34;; // true s != \u0026#34;hello\u0026#34;; // false s \u0026gt; \u0026#34;aa\u0026#34;; // true s \u0026lt; \u0026#34;xx\u0026#34;; // true s \u0026gt;= \u0026#34;he\u0026#34;; // true s \u0026lt;= \u0026#34;he\u0026#34;; // false #operator\u0026laquo; std::ostream\u0026amp; operator\u0026lt;\u0026lt;(std::ostream\u0026amp; os, const fastring\u0026amp; s); Example fastring s(\u0026#34;xx\u0026#34;); std::cout \u0026lt;\u0026lt; s \u0026lt;\u0026lt; std::endl; "},{"id":15,"href":"/en/co/mem/","title":"Memory Allocation","section":"Documents","content":"include: co/mem.h.\n#co::alloc 1. void* alloc(size_t size); 2. void* alloc(size_t size, size_t align); 1, allocate size bytes of memory. 2, allocate size bytes of memory, and the memory boundary is align byte-aligned (align \u0026lt;= 1024). In the 2nd, align must be power of 2 and cannot exceed 1024. #co::free void free(void* p, size_t size); Free memory allocated by co::alloc or co::realloc, where size is the size of the allocated memory. co::free is different from the ::free provided by the system, it needs an additional size parameter. #co::realloc void* realloc(void* p, size_t old_size, size_t new_size); Reallocate memory, old_size is the previous memory size, new_size is the new size, the latter must be greater than the former. #co::zalloc void* zalloc(size_t size); Allocate size bytes of memory and fill the memory with zeros. #——————————— #co::make template\u0026lt;typename T, typename... Args\u0026gt; inline T* make(Args\u0026amp;\u0026amp;... args); Call co::alloc to allocate memory and make an object of type T on it with argument args.\nExample\nint* p = co::make\u0026lt;int\u0026gt;(7); std::string* s = co::make\u0026lt;std::string\u0026gt;(3, \u0026#39;x\u0026#39;); #co::del template\u0026lt;typename T\u0026gt; inline void del(T* p, size_t n=sizeof(T)); Destroy objects created by co::make and free the memory.\nThe parameter n is the memory size of the object pointed to by p, and the default is sizeof(T).\nExample\nint* p = co::make\u0026lt;int\u0026gt;(7); co::del(p); #co::make_rootic template\u0026lt;typename T, typename... Args\u0026gt; inline T* make_rootic(Args\u0026amp;\u0026amp;... args); Similar to co::make_static, except that static objects created by this function are destructed later than static objects created by co::make_static. #co::make_static template\u0026lt;typename T, typename... Args\u0026gt; inline T* make_static(Args\u0026amp;\u0026amp;... args); Create a static object of type T with the parameter args, and it will be automatically destroyed when the program exits.\nExample\nfasting\u0026amp; s = *co::make_static\u0026lt;fastring\u0026gt;(32, \u0026#39;x\u0026#39;); #——————————— #co::shared template\u0026lt;typename T\u0026gt; class shared; Similar to std::shared_ptr, but with some minor differences.\n#constructor 1. constexpr shared() noexcept; 2. constexpr shared(std::nullptr_t) noexcept; 3. shared(const shared\u0026amp; x) noexcept; 4. shared(shared\u0026amp;\u0026amp; x) noexcept; 5. shared(const shared\u0026lt;X\u0026gt;\u0026amp; x) noexcept; 6. shared(shared\u0026lt;X\u0026gt;\u0026amp;\u0026amp; x) noexcept; 1-2, create an empty shared object. 3, Copy constructor, if x is not an empty object, increase the internal reference count. 4, Move constructor, when the construction is completed, x becomes an empty object. 5-6, Construct shared\u0026lt;T\u0026gt; object from shared\u0026lt;X\u0026gt; object, T is the base class of X, and the destructor of T is virtual. co::shared objects cannot be constructed directly from T* pointer, coost provides co::make_shared for constructing co::shared objects. #destructor ~shared(); If the object is not empty, the internal reference count will be decremented by 1. When the reference count is reduced to 0, the internal object will be destroyed and the memory will be released. #operator= 1. shared\u0026amp; operator=(const shared\u0026amp; x); 2. shared\u0026amp; operator=(shared\u0026amp;\u0026amp; x); 3. shared\u0026amp; operator=(const shared\u0026lt;X\u0026gt;\u0026amp; x); 4. shared\u0026amp; operator=(shared\u0026lt;X\u0026gt;\u0026amp;\u0026amp; x); Assignment operations. 3-4, assign value of shared\u0026lt;X\u0026gt; to object of shared\u0026lt;T\u0026gt;, T is the base class of X, and the destructor of T is virtual . #get T* get() const noexcept; Get a pointer to the internal object. #operator-\u0026gt; T* operator-\u0026gt;() const; Overload operator-\u0026gt;, return a pointer to the internal object. #operator* T\u0026amp; operator*() const; Overload operator*, return a reference to the internal object. #operator== bool operator==(T* p) const noexcept; Determine whether the internal pointer is equal to p. #operator!= bool operator!=(T* p) const noexcept; Determine whether the internal pointer is not equal to p. #operator bool explicit operator bool() const noexcept; Returns false if the internal pointer is NULL, otherwise returns true. #ref_count size_t ref_count() const noexcept; Get the reference count of the internal object. #reset void reset(); If the internal pointer is not NULL, decrement the reference count by 1 (destroy the internal object when reduced to 0), and then set the internal pointer to NULL. #swap void swap(shared\u0026amp; x); void swap(shared\u0026amp;\u0026amp; x); Swaps internal pointers of co::shared objects. #use_count size_t use_count() const noexcept; Equivalent to ref_count. #co::make_shared template\u0026lt;typename T, typename... Args\u0026gt; inline shared\u0026lt;T\u0026gt; make_shared(Args\u0026amp;\u0026amp;... args); Creates an object of type shared\u0026lt;T\u0026gt; with arguments args.\nExample\nco::shared\u0026lt;int\u0026gt; i = co::make_shared\u0026lt;int\u0026gt;(23); co::shared\u0026lt;fastring\u0026gt; s = co::make_shared\u0026lt;fastring\u0026gt;(32, \u0026#39;x\u0026#39;); #co::unique template\u0026lt;typename T\u0026gt; class unique; Similar to std::unique_ptr, but with some minor differences.\n#constructor 1. constexpr unique() noexcept; 2. constexpr unique(std::nullptr_t) noexcept; 3. unique(unique\u0026amp; x) noexcept; 4. unique(unique\u0026amp;\u0026amp; x) noexcept; 5. unique(unique\u0026lt;X\u0026gt;\u0026amp; x) noexcept; 6. unique(unique\u0026lt;X\u0026gt;\u0026amp;\u0026amp; x) noexcept; 1-2, create an empty unique object. 3-4, transfer the internal pointer of x to the constructed unique object, and the internal pointer of x becomes NULL. 5-6, Construct unique\u0026lt;T\u0026gt; object from unique\u0026lt;X\u0026gt; object, T is the base class of X, and the destructor of T is virtual. co::unique object cannot be constructed directly from T* pointer, coost provides co::make_unique for constructing co::unique object. #destructor ~unique(); Destroy the internal object, and free the memory. #operator= 1. unique\u0026amp; operator=(unique\u0026amp; x); 2. unique\u0026amp; operator=(unique\u0026amp;\u0026amp; x); 3. unique\u0026amp; operator=(unique\u0026lt;X\u0026gt;\u0026amp; x); 4. unique\u0026amp; operator=(unique\u0026lt;X\u0026gt;\u0026amp;\u0026amp;x); Assignment operations. 3-4, assign value of unique\u0026lt;X\u0026gt; to object of unique\u0026lt;T\u0026gt;, T is the base class of X, and the destructor of T is virtual . #get T* get() const noexcept; Get a pointer to the internal object. #operator-\u0026gt; T* operator-\u0026gt;() const; Overload operator-\u0026gt;, return a pointer to the internal object. #operator* T\u0026amp; operator*() const; Overload operator*, return a reference to the internal object. #operator== bool operator==(T* p) const noexcept; Determine whether the internal pointer is equal to p. #operator!= bool operator!=(T* p) const noexcept; Determine whether the internal pointer is not equal to p. #operator bool explicit operator bool() const noexcept; Returns false if the internal pointer is NULL, otherwise returns true. #reset void reset(); Destroy the internal object, and free the memory. #swap void swap(unique\u0026amp; x); void swap(unique\u0026amp;\u0026amp; x); Swap the internal pointers of co::unique objects. #co::make_unique template\u0026lt;typename T, typename... Args\u0026gt; inline unique\u0026lt;T\u0026gt; make_unique(Args\u0026amp;\u0026amp;... args); Create an object of type unique\u0026lt;T\u0026gt; with arguments args.\nExample\nco::unique\u0026lt;int\u0026gt; i = co::make_unique\u0026lt;int\u0026gt;(23); co::unique\u0026lt;fastring\u0026gt; s = co::make_unique\u0026lt;fastring\u0026gt;(32, \u0026#39;x\u0026#39;); "},{"id":16,"href":"/en/co/net/mode/","title":"Network programming model","section":"Network Programming","content":"#Coroutine based network programming model It is easy to write high-concurrency and high-performance network programs with coroutine. Although a coroutine may block, the scheduling thread can quickly switch between a large number of coroutines. Therefore, to achieve high concurrency, we just need to create more coroutines.\n#Network model for TCP server void on_connection(int fd) { while (true) { co::recv(fd, ...); // recv request from client process(...); // process the request co::send(fd, ...); // send response to client } } void server_fun() { while (true) { int fd = co::accept(...); if (fd != -1) go(on_connection, fd); } } go(server_fun); One coroutine for each connection. In one coroutine, call co::accept() to accept client connections. When a connection is accepted, create a new coroutine to handle the connection. on_connection() is the coroutine function for handling connections, receiving, processing and sending data are performed in a synchronous manner in the coroutine, and we do not need any asynchronous callback. #Network model for TCP client void client_fun() { while (true) { if (!connected) co::connect(...); // connect to the server co::send(...); // send request to the server co::recv(...); // recv response from the server process(...); // process the response if (over) co::close(...); // close the connection } } go(client_fun); Connecting, sending, recving and processing data are performed in a synchronous manner in the coroutine. In actual applications, co::pool is generally used as a connection pool to avoid creating too many connections:\nco::pool g_p; void client_fun() { while (true) { co::pool_guard\u0026lt;Connection\u0026gt; conn(g_p); // get a idle connection from the pool conn-\u0026gt;send(...); // send request to the server conn-\u0026gt;recv(...); // recv response from the server process(...); // process the response if (over) conn-\u0026gt;close(...); // close the connection } } go(client_fun); "},{"id":17,"href":"/en/about/sponsor/","title":"Sponsor💕","section":"About","content":"#Sponsor coost is a personal project. If you are interested in sponsoring it, you may contact with Alvin (idealvin at qq.com), we\u0026rsquo;ll display your logo, website and other information here, and also provide you with better technical services. Thank you very much.\n#Coffee If you like coost, you may also buy the author a cup of coffee, thanks.\nPaypal\[email protected]\nWechat \u0026amp; Alipay\nGithub Sponsor\n"},{"id":18,"href":"/en/co/other/fastream/","title":"fastream","section":"others","content":"include: co/fastream.h.\n#fastream fastream is used to replace std::ostringstream in the C++ standard library. The performance of std::ostringstream is poor which may be several times slower than snprintf, and fastream is about 10~30 times faster than snprintf on different platforms.\n#constructor 1. constexpr fastream() noexcept; 2. explicit fastream(size_t cap); 3. fastream(fastream\u0026amp;\u0026amp; s) noexcept; 1, the default constructor, which creates an empty fastream object without any memory allocation.\n2, uses the parameter cap to specify the initial capacity of fastream.\n3, the move constructor.\nExample\nfastream s; // empty fastream, no memory allocated fastream s(1024); // Pre-allocate 1k bytes fastream x(std::move(s); // move construction, s becomes empty #operator= fastream\u0026amp; operator=(fastream\u0026amp;\u0026amp; s) noexcept; Move assignment, the content of s is transferred to fastream, and s itself becomes empty.\nExample\nfastream s(32); fastream x; x = std::move(s); // x capacity -\u0026gt; 32, s -\u0026gt; empty #——————————— #back char\u0026amp; back(); const char\u0026amp; back() const; This method returns a reference to the last character in fastream. If fastream is empty, calling this method will cause undefined behavior. Example fastream s; s.append(\u0026#34;hello\u0026#34;); char c = s.back(); // c =\u0026#39;o\u0026#39; s.back() =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;hellx\u0026#34; #front char\u0026amp; front(); const char\u0026amp; front() const; This method returns the reference of the first character in fastream. If fastream is empty, calling this method will cause undefined behavior. Example fastream s; s.append(\u0026#34;hello\u0026#34;); char c = s.front(); // c =\u0026#39;h\u0026#39; s.front() =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;xello\u0026#34; #operator[] char\u0026amp; operator[](size_t n); const char\u0026amp; operator[](size_t n) const; This method returns the reference of the nth character in fastream. If n is out of a reasonable range, calling this method will cause undefined behavior. Example fastream s; s.append(\u0026#34;hello\u0026#34;); char c = s[1]; // c =\u0026#39;e\u0026#39; s[1] =\u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;hxllo\u0026#34; #——————————— #capacity size_t capacity() const noexcept; This method returns the capacity of fastream. #c_str const char* c_str() const; This method gets the equivalent C-style string (null terminated). Writing to the character array accessed through c_str() is undefined behavior. #data char* data() noexcept; const char* data() const noexcept; This method is similar to c_str(), but it does not guarantee that the string ends with \u0026lsquo;\\0\u0026rsquo;. #empty bool empty() const noexcept; This method determines whether fastream is empty. #size size_t size() const noexcept; This method returns the length of data in fastream. #str fastring str() const; This method returns a copy of fastream\u0026rsquo;s internal data in the form of fastring.\nExample\nfastream s; s.append(\u0026#34;hello\u0026#34;); fastring x = s.str(); // x = \u0026#34;hello\u0026#34; #——————————— #clear 1. void clear(); 2. void clear(char c); This method sets the size of fastream to 0, and the capacity remains unchanged. The second is similar to the first, except that it will fill the memory with character c before setting its size to 0. The second one is added in v3.0.1, which can be used to clear sensitive information in memory. #ensure void ensure(size_t n); This method ensures that the remaining memory of fastream can hold at least n characters. #reserve void reserve(size_t n); This method ensures that the capacity of fastream is at least n. When n is less than the original capacity, the capacity remains unchanged. #reset void reset(); Added in v2.0.3. Clear fastream and free the memory. #resize 1. void resize(size_t n); 2. void resize(size_t n, char c); This method sets the size of fastream to n.\nWhen n is greater than the original size, it will expand size to n. In the 1st version, the content of the extended part is undefined. The 2nd version will fill the extended part with character c.\nExample\nfastream s; s.append(\u0026#34;hello\u0026#34;); s.resize(3); // s -\u0026gt; \u0026#34;hel\u0026#34; s.resize(6); char c = s[5]; // c is an uncertain random value s.resize(3); s.resize(6, 0); c = s[5]; // c is \u0026#39;\\0\u0026#39; #swap void swap(fastream\u0026amp; s) noexcept; void swap(fastream\u0026amp;\u0026amp; s) noexcept; Swap the contents of two fastreams, only the internal pointer, capacity, and size are exchanged.\nExample\nfastream s(32); fastring x(64); s.swap(x); // s: cap -\u0026gt; 64, x: cap -\u0026gt; 32 #——————————— #append 1. fastream\u0026amp; append(const void* s, size_t n); 2. fastream\u0026amp; append(const char* s); 3. fastream\u0026amp; append(const fastring\u0026amp; s); 4. fastream\u0026amp; append(const std::string\u0026amp; s); 5. fastream\u0026amp; append(const fastream\u0026amp; s); 6. fastream\u0026amp; append(size_t n, char c); 7. fastream\u0026amp; append(char c); 8. fastream\u0026amp; append(signed char v) 9. fastream\u0026amp; append(unsigned char c); 10. fastream\u0026amp; append(uint16 v); 11. fastream\u0026amp; append(uint32 v); 12. fastream\u0026amp; append(uint64 v); 1, appends a byte sequence of length n. 2-4, append string s. 5, appends a fastream, s can be the fastream itself that performs the append operation. 6, append n characters c. 7-9, append a single character c. 10-12, equivalent to append(\u0026amp;v, sizeof(v)). Since v3.0.1, it is ok that the parameter s in 1 and 2 overlaps with the internal memory of fastream.\nv3.0.2 removed fastream\u0026amp; append(char c, size_t n);. Example fastream s; int32 i = 7; char buf[8]; s.append(\u0026#34;xx\u0026#34;); // Append C string s.append(s); // append itself, s -\u0026gt; \u0026#34;xxxx\u0026#34; s.append(buf, 8); // Append 8 bytes s.append(\u0026#39;c\u0026#39;); // Append a single character s.append(100,\u0026#39;c\u0026#39;); // Append 100\u0026#39;c\u0026#39; s.append(\u0026amp;i, 4); // Append 4 bytes s.append(i); // Append 4 bytes, same as above s.append((int16)23); // Append 2 bytes s.append(s.c_str() + 1); // ok since v3.0.1 #append_nomchk fastream\u0026amp; append_nomchk(const void* s, size_t n); fastream\u0026amp; append_nomchk(const char* s) Similar to append(), but will not check if s overlaps with the internal memory. This method cannot be used if s may overlap with the internal memory of fastream. #cat template\u0026lt;typename X, typename ...V\u0026gt; fastream\u0026amp; cat(X\u0026amp;\u0026amp; x, V\u0026amp;\u0026amp; ... v); Added in v2.0.3. Concatenate any number of elements to fastream.\nThis method appends elements in the parameters to fastream one by one through operator\u0026lt;\u0026lt;.\nExample\nfastream s; s \u0026lt;\u0026lt; \u0026#34;hello\u0026#34;; s.cat(\u0026#39;\u0026#39;, 23, \u0026#34;xx\u0026#34;, false); // s -\u0026gt; \u0026#34;hello 23xxfalse\u0026#34; #operator\u0026laquo; fastream\u0026amp; operator\u0026lt;\u0026lt;(bool v); fastream\u0026amp; operator\u0026lt;\u0026lt;(char v); fastream\u0026amp; operator\u0026lt;\u0026lt;(signed char v); fastream\u0026amp; operator\u0026lt;\u0026lt;(unsigned char v); fastream\u0026amp; operator\u0026lt;\u0026lt;(short v); fastream\u0026amp; operator\u0026lt;\u0026lt;(unsigned short v); fastream\u0026amp; operator\u0026lt;\u0026lt;(int v); fastream\u0026amp; operator\u0026lt;\u0026lt;(unsigned int v); fastream\u0026amp; operator\u0026lt;\u0026lt;(long v); fastream\u0026amp; operator\u0026lt;\u0026lt;(unsigned long v); fastream\u0026amp; operator\u0026lt;\u0026lt;(long long v); fastream\u0026amp; operator\u0026lt;\u0026lt;(unsigned long long v); fastream\u0026amp; operator\u0026lt;\u0026lt;(double v); fastream\u0026amp; operator\u0026lt;\u0026lt;(float v); fastream\u0026amp; operator\u0026lt;\u0026lt;(const dp::_fpt\u0026amp; v); fastream\u0026amp; operator\u0026lt;\u0026lt;(const void* v); fastream\u0026amp; operator\u0026lt;\u0026lt;(std::nullptr_t); fastream\u0026amp; operator\u0026lt;\u0026lt;(const char* s); fastream\u0026amp; operator\u0026lt;\u0026lt;(const signed char* s); fastream\u0026amp; operator\u0026lt;\u0026lt;(const unsigned char* s); fastream\u0026amp; operator\u0026lt;\u0026lt;(const fastring\u0026amp; s); fastream\u0026amp; operator\u0026lt;\u0026lt;(const std::string\u0026amp; s); fastream\u0026amp; operator\u0026lt;\u0026lt;(const fastream\u0026amp; s); Format value of bool, char, integer type, floating point type, pointer type or string type, and append it to fastream.\noperator\u0026lt;\u0026lt;(const dp::_fpt\u0026amp; v) is used to output formatted floating-point number, which the effective decimal places can be specified.\nExample\nfastream s; s \u0026lt;\u0026lt; \u0026#39;x\u0026#39;; // s -\u0026gt; \u0026#34;x\u0026#34; s \u0026lt;\u0026lt; s; // s -\u0026gt; \u0026#34;xx\u0026#34; (append itself) s \u0026lt;\u0026lt; false; // s -\u0026gt; \u0026#34;xxfalse\u0026#34; s.clear(); s \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; // s -\u0026gt; \u0026#34;hello 23\u0026#34; s \u0026lt;\u0026lt; (s.c_str() + 6); // s -\u0026gt; \u0026#34;hello 2323\u0026#34; (append part of s) s.clear(); s \u0026lt;\u0026lt; 3.1415; // s -\u0026gt; \u0026#34;3.1415\u0026#34; s.clear(); s \u0026lt;\u0026lt; (void*)32; // s -\u0026gt; \u0026#34;0x20\u0026#34; Specify the number of valid decimal places\ncoost provides dp::_1, dp::_2, ..., dp::_16, dp::_n to set the number of effective decimal places of floating-point numbers.\nfastream s; s \u0026lt;\u0026lt; dp::_2(3.1415); // \u0026#34;3.14 s \u0026lt;\u0026lt; dp::_3(3.1415); // \u0026#34;3.141\u0026#34; s \u0026lt;\u0026lt; dp::_n(3.14, 1); // \u0026#34;3.1\u0026#34;, equivalent to dp::_1(3.14) co.log is implemented based on fastream, so we can also use the above methods when printing logs.\nLOG \u0026lt;\u0026lt; dp::_2(3.1415); "},{"id":19,"href":"/en/co/flag/","title":"flag","section":"Documents","content":"include: co/flag.h.\n#Basic concepts co.flag is a command line and config file parsing library. Its principle is very simple, define global variables in code, then parse the command line parameters and/or config file when the program starts, and update the value of these global variables.\n#flag variable The global variable defined by macros in co.flag are called flag variable. For example, the following code defines a flag variable, the variable name is FLG_x.\nDEF_int32(x, 0, \u0026#34;xxx\u0026#34;); // int32 FLG_x = 0; co.flag supports 7 types of flag variable:\nbool, int32, int64, uint32, uint64, double, string Every flag variable has a default value, and a new value can be passed to it from command-line or config file. Take the previously FLG_x as an example, we can use -x=23 in command line, or x = 23 in the config file, to set a new value for it.\n#command line flag Command line parameters appear in the form of -x=y, where x is called a command line flag (hereinafter referred to as flag). The flag x in command line corresponds to the global variable FLG_x in the code, and -x=y in command line is equivalent to setting the value of FLG_x to y.\nco.flag is designed to be very flexible:\n-x=y can omit the preceding -, abbreviated as x=y.\n-x=y can also be written as -x y.\nx=y can be preceded by any number of -.\nFor bool type flags, -b=true can be abbreviated as -b.\nExample\n# b, i, s are all flags, xx is not a flag ./exe -b -i=32 -s=hello xx #APIs #flag::parse co::vector\u0026lt;fastring\u0026gt; parse(int argc, char** argv); void parse(const fastring\u0026amp; path); Added in v3.0.1.\nThe first parse function, parse the command line parameters and config file, and update value of the flag variables. It usually needs to be called once at the beginning of the main function. Generally speaking, it does the following steps:\nPreprocess the command line parameters, the value of FLG_config may be updated then. If FLG_config is not empty, parse the config file specified by it, and update value of the flag variables. Parse other command line parameters and update value of the flag variables. If FLG_mkconf is true, generate a config file and terminate the program. If FLG_daemon is true, run the program as a daemon (for Linux only). When any error occurs, print the error message and terminate the program immediately. If no error occurs, return the non-flag list. For example, when executing ./exe x y, this function will return [\u0026quot;x\u0026quot;, \u0026quot;y\u0026quot;]. The second parse function, parses the config file and updates value of the flag variables. The parameter path is the path of the config file. When any error occurs, print the error message and terminate the program.\nflag::init\nSince v3.0.1,flag::init() has been marked as deprecated, please use flag::parse() instead. Example #include \u0026#34;co/flag.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); } #flag::set_value fastring set_value(const fastring\u0026amp; name, const fastring\u0026amp; value) Added in v3.0. Set value of a flag variable, name is the flag name.\nThis function is not thread-safe and usually needs to be called at the beginning of the main function.\nExample\nDEF_bool(b, false, \u0026#34;\u0026#34;); DEF_int32(i, 0, \u0026#34;\u0026#34;); DEF_string(s, \u0026#34;\u0026#34;, \u0026#34;\u0026#34;); int main(int argc, char** argv) { flag::set_value(\u0026#34;b\u0026#34;, \u0026#34;true\u0026#34;); // FLG_b -\u0026gt; true flag::set_value(\u0026#34;i\u0026#34;, \u0026#34;23\u0026#34;); // FLG_i -\u0026gt; 23 flag::set_value(\u0026#34;s\u0026#34;, \u0026#34;xx\u0026#34;); // FLG_s -\u0026gt; \u0026#34;xx\u0026#34; flag::parse(argc, argv); } #flag::alias bool alias(const char* name, const char* new_name); Added in v3.0. Add an alias to a flag, in command line or config file you can replace the original name with the alias.\nThis function is not thread safe and needs to be called before flag::parse().\nExample\nDEF_bool(all, false, \u0026#34;\u0026#34;); int main(int argc, char** argv) { flag::alias(\u0026#34;all\u0026#34;, \u0026#34;a\u0026#34;); flag::parse(argc, argv); } #Use flag variable in the code #Define a flag variable DEF_bool(name, value, help, ...) DEF_int32(name, value, help, ...) DEF_int64(name, value, help, ...) DEF_uint32(name, value, help, ...) DEF_uint64(name, value, help, ...) DEF_double(name, value, help, ...) DEF_string(name, value, help, ...) The above 7 macros are used to define 7 different types of flag variables.\nThe parameter name is the flag name, the corresponding global variable name is FLG_name, the parameter value is the default value, and the parameter help is comment for the flag.\nA flag variable is a global variable and generally should not be defined in the header file.\nThe name of the flag variable is unique, and we cannot define two flag variables with the same name.\nThe flag variable is generally defined outside the namespace, otherwise it may be not possible to use FLG_name to access the flag variable.\nExample\nDEF_bool(b, false, \u0026#34;comments\u0026#34;); // bool FLG_b = false; DEF_int32(i32, 32, \u0026#34;comments\u0026#34;); // int32 FLG_i32 = 32; DEF_int64(i64, 64, \u0026#34;comments\u0026#34;); // int64 FLG_i64 = 64; DEF_uint32(u32, 0, \u0026#34;comments\u0026#34;); // uint32 FLG_u32 = 0; DEF_uint64(u64, 0, \u0026#34;comments\u0026#34;); // uint64 FLG_u64 = 0; DEF_double(d, 2.0, \u0026#34;comments\u0026#34;); // double FLG_d = 2.0; DEF_string(s, \u0026#34;x\u0026#34;, \u0026#34;comments\u0026#34;); // fastring\u0026amp; FLG_s = ...; Since v3.0.1, DEF_string actually defines a reference to fastring. #Add alias for a flag Added in v3.0, when defining a flag variable, you can add any number of aliases to the flag.\nIn command line or config file, alias can be used instead of the original name.\nExample\nDEF_bool(debug, false, \u0026#34;\u0026#34;); // no alias DEF_bool(debug, false, \u0026#34;\u0026#34;, d); // d is an alias of debug DEF_bool(debug, false, \u0026#34;\u0026#34;, d, dbg); // 2 aliases #Declare the flag variable DEC_bool(name) DEC_int32(name) DEC_int64(name) DEC_uint32(name) DEC_uint64(name) DEC_double(name) DEC_string(name) The 7 macros above are used to declare 7 different types of flag variables.\nThe parameter name is the flag name, and the corresponding global variable name is FLG_name.\nA flag variable can be defined only once, but it can be declared multiple times, which can be declared wherever needed.\nThe flag variable is generally declared outside the namespace, otherwise it may be not possible to use FLG_name to access the flag variable.\nExample\nDEC_bool(b); // extern bool FLG_b; DEC_int32(i32); // extern int32 FLG_i32; DEC_int64(i64); // extern int64 FLG_i64; DEC_uint32(u32); // extern uint32 FLG_u32; DEC_uint64(u64); // extern uint64 FLG_u64; DEC_double(d); // extern double FLG_d; DEC_string(s); // extern fastring\u0026amp; FLG_s; #Use the flag variable Once a flag variable is defined or declared, we can use it the same as an ordinary variable.\n#include \u0026#34;co/flag.h\u0026#34; DEC_bool(b); DEF_string(s, \u0026#34;hello\u0026#34;, \u0026#34;xxx\u0026#34;); int main(int argc, char** argv) { flag::parse(argc, argv); if (!FLG_b) std::cout \u0026lt;\u0026lt; \u0026#34;b is false\u0026#34; \u0026lt;\u0026lt; std::endl; FLG_s += \u0026#34; world\u0026#34;; std::cout \u0026lt;\u0026lt; FLG_s \u0026lt;\u0026lt; std::endl; return 0; } #Use flag in command line #Set value of flags Suppose the following flags are defined in the program:\nDEF_bool(x, false, \u0026#34;bool x\u0026#34;); DEF_bool(y, false, \u0026#34;bool y\u0026#34;); DEF_int32(i, -32, \u0026#34;int32\u0026#34;); DEF_uint64(u, 64, \u0026#34;uint64\u0026#34;); DEF_string(s, \u0026#34;nice\u0026#34;, \u0026#34;string\u0026#34;); When the program starts, we can modify value of the flag variables through command line parameters:\n# -x=y, x=y, -x y, the three are equivalent ./xx -i=8 u=88 -s=xxx ./xx -i 8 -u 88 -s \u0026#34;hello world\u0026#34; ./xx -i8 # -i=8, only for single-letter named integer flags # When a bool type is set to true, the value can be omitted ./xx -x # -x=true # Multiple single-letter named bool flags can be combined and set to true ./xx -xy # -x=true -y=true # Integer type flags can have units k, m, g, t, p, not case sensitive ./xx -i -4k # i=-4096 # Integer type flags can pass octal or hexadecimal numbers ./xx i=032 # i=26 octal ./xx u=0xff # u=255 hexadecimal #Show Help Information co.flag supports using --help command to print the help information of the program:\n$ ./xx --help usage: $exe [-flag] [value] $exe -x -i 8k -s ok # x=true, i=8192, s=\u0026#34;ok\u0026#34; $exe -- # print all flags $exe -mkconf # generate config file $exe -conf xx.conf # run with config file flags: -n int32 type: int32 default: 0 from: test/flag.cc -s string type: string default: \u0026#34;hello world\u0026#34; from: test/flag.cc #List all flags co.flag provides -- command to list all the flags defined in the program:\n$ ./xx -- flags: -boo bool flag type: bool default: false from: test/flag.cc -co_sched_num number of coroutine schedulers, default: os::cpunum() type: uint32 default: os::cpunum() from: src/co/sched.cc #Show version of the program version is a flag defined inside coost. You can use the -version command to print version information.\nversion is empty by default, its value should be set before calling flag::parse().\nExample\n#include \u0026#34;co/flag.h\u0026#34; int main(int argc, char** argv) { FLG_version = \u0026#34;v3.0.0\u0026#34;; flag::parse(argc, argv); return 0; } $ ./xx -version v3.0.0 #config file #config file format The config file format of co.flag is flexible:\nOne config item per line, each config item corresponds to a flag, and the form is unified as x = y, which looks clear at a glance.\n# or // are for comments.\n# or // in quotation marks are not comments.\nIgnore the blank characters at the beginning or end of the line.\nBlank characters can be added before or after the = sign.\n\\ can be used to continue a line to avoid too long a line.\nThe string does not support escaping to avoid ambiguity.\nThe string can be enclosed in double quotes, single quotes or 3 back quotes.\nSample config file\n# config file: xx.conf boo = true # bool type s = # empty string s = hello \\ world # s = \u0026#34;helloworld\u0026#34; s = \u0026#34;http://github.com\u0026#34; # # or // in quotation marks are not comments s = \u0026#34;I\u0026#39;m ok\u0026#34; # enclose the string in double quotes s =\u0026#39;how are \u0026#34;U\u0026#34;\u0026#39; # enclose the string in single quotes s = ```I\u0026#39;m \u0026#34;ok\u0026#34;``` # enclose the string in 3 back quotes i32 = 4k # 4096, integers can have units k, m, g, t, p, not case sensitive i32 = 032 # octal, i32 = 26 i32 = 0xff # hexadecimal, i32 = 255 pi = 3.14159 # double type #Generate config file mkconf is a flag defined internally in coost, which is a switch for automatically generating config file. You can use -mkconf to generate a config file in command line. ./xx -mkconf # Generate xx.conf ./xx -mkconf -x u=88 # Custom config item value #Adjust the order of config items In the automatically generated config file, the config items are sorted by flag level, file name, and code line number. If the user wants some config items to be ranked higher, the flag level can be set to a smaller value, otherwise, the flag level can be set to a larger value.\nWhen defining a flag, you can use #n at the beginning of the comment to specify the flag level, n must be an integer between 0 and 9. If the comment is not empty, there must be a space after n. When not specified, the default flag level is 5.\nDEF_bool(x, false, \u0026#34;comments\u0026#34;); // The default level is 5 DEF_bool(y, false, \u0026#34;#3\u0026#34;); // The level is 3, and the comment is empty DEF_bool(z, false, \u0026#34;#3 comments\u0026#34;); // The level is 3 #Prohibit config items from being generated in the config file Flags whose comments start with ., are hidden flags, which will not be present in the config file, but can be found with the -- command in command line. A flag with an empty comment is completely invisible and will neither be generated in the config file nor be found with the -- command.\nDEF_bool(x, false, \u0026#34;.say something here\u0026#34;); DEF_string(s, \u0026#34;good\u0026#34;, \u0026#34;\u0026#34;); #Specify the config file when the program starts config is a flag defined internally in coost, which is the path of the config file. It has an alias conf. You can use -config to specify the config file in command line. Another way, you can modify the value of FLG_config to specify the config file, before calling flag::parse(). ./xx -config xx.conf ./xx -conf xx.conf # If the config file name ends with .conf or config, # and it is the first non-flag parameter in command line, # -config can be omitted. ./xx xx.conf ./xx xx.conf -x #Custom help information help is a flag defined in coost, which stores the help information of the program. This information can be seen with the command --help in command line.\nFLG_help is empty by default, and the default help information provided by coost is used.\nYou can modify the value of FLG_help before calling flag::parse() to customize the help information.\nExample\n#include \u0026#34;co/flag.h\u0026#34; int main(int argc, char** argv) { FLG_help \u0026lt;\u0026lt; \u0026#34;usage:\\n\u0026#34; \u0026lt;\u0026lt; \u0026#34;\\t./xx -ip 127.0.0.1 -port 7777\\n\u0026#34;; flag::parse(argc, argv); return 0; } #Run program as a daemon daemon is a flag defined in coost. If it is true, the program will run as a daemon. It only works on Linux platform.\nYou can use -daemon in command line to make the program run in the background as a daemon.\nExample\n./xx -daemon "},{"id":20,"href":"/en/co/concurrency/coroutine/event/","title":"Sync Event","section":"Coroutine","content":"include: co/co.h.\n#co::event co::event is a synchronization mechanism between coroutines, which is similar to co::sync_event in threads.\nStarting from v2.0.1, co::event can be used in coroutines and non-coroutines. #constructor 1. explicit event(bool manual_reset=false, bool signaled=false); 2. event(event\u0026amp;\u0026amp; e); 3. event(const event\u0026amp; e); 1, similar to co::sync_event in threads. 2, move constructor. 3, copy constructor, only increases the internal reference count by 1. #reset void reset() const; Reset the event to unsynced state. #signal void signal() const; Generates a sync signal, setting the event to synced state. All waiting coroutines or threads will be awakened. If there is no waiting coroutine or thread, the next coroutine or thread that calls the wait() method will return immediately. #wait 1. void wait() const; 2. bool wait(uint32 ms) const; 1, wait until the event becomes synced. 2, Wait until the event becomes synced or times out. The parameter ms specifies the timeout in milliseconds. Returns true if the event becomes synced, false otherwise. When manual_reset in the constructor is false, the event will be automatically set to unsynced state when wait() ends. #Code Example #include \u0026#34;co/co.h\u0026#34; int main(int argc, char** argv) { flag::parse(argc, argv); co::event ev; // capture by value, // as data on stack may be overwritten by other coroutines. go([ev](){ ev.signal(); }); ev.wait(100); // wait for 100 ms return 0; } "},{"id":21,"href":"/en/co/net/third/","title":"Use third-party network libraries","section":"Network Programming","content":"#Use third-party network libraries in coroutines There are two ways to use third-party network libraries in coroutines:\nDirectly use the blocking APIs of the third-party network library. This is the simplest way, but it relies on the system API hook in coost. Use the non-blocking APIs of the third-party network library. In this way, users need to convert the APIs to synchronous manner with co::io_event. #System API hook API hook simply intercepts system API requests. If the request is in coroutine and uses a blocking socket, modify the socket to non-blocking mode. When the socket is unreadable or unwritable, use co::io_event or the lower-level APIs in coost to wait for the I/O event. When the I/O event arrives, wake up the coroutine and call the system\u0026rsquo;s native socket API to complete the I/O operation.\n#Using non-blocking APIs The following is the recv method implemented based on openssl\u0026rsquo;s non-blocking API:\nint recv(S* s, void* buf, int n, int ms) { CHECK(co::sched()) \u0026lt;\u0026lt; \u0026#34;must be called in coroutine..\u0026#34;; int r, e; int fd = SSL_get_fd((SSL*)s); if (fd \u0026lt; 0) return -1; do { ERR_clear_error(); r = SSL_read((SSL*)s, buf, n); if (r \u0026gt; 0) return r; // success if (r == 0) return 0; e = SSL_get_error((SSL*)s, r); if (e == SSL_ERROR_WANT_READ) { co::io_event ev(fd, co::ev_read); if (!ev.wait(ms)) return -1; } else if (e == SSL_ERROR_WANT_WRITE) { co::io_event ev(fd, co::ev_write); if (!ev.wait(ms)) return -1; } else { return r; } } while (true); } The whole process is relatively simple. The underlying socket must be non-blocking. When SSL_read generates SSL_ERROR_WANT_READ error, use co::io_event to wait for the read event. When SSL_ERROR_WANT_WRITE error occurs, use co:: io_event to wait for the write event, when wait() returns normally, it means the socket is readable or writable, continue to call SSL_read to complete the I/O operation.\n"},{"id":22,"href":"/en/co/concurrency/coroutine/io_event/","title":"IO Event","section":"Coroutine","content":"include: co/co.h.\n#co::_ev_t enum _ev_t { ev_read = 1, ev_write = 2, }; Type of I/O event. #co::io_event co::io_event is used to convert non-blocking I/O to synchronous mode. The user performs I/O operations on a non-blocking socket in coroutine. When the socket is unreadable or unwritable, the user calls the wait() method of co::io_event to suspend the coroutine and wait for the I/O event; When the socket becomes readable or writable, the scheduling thread will wake up the coroutine again and continue the I/O operation.\n#constructor 1. io_event(sock_t fd, _ev_t ev); 2. io_event(sock_t fd, int n=0); // for windows only 1, parameter fd is a non-blocking socket, parameter ev is ev_read or ev_write. Calling the wait() method will wait for the I/O event specified by ev on the socket. When wait() returns successfully, the user needs to call recv, send or other API to complete the I/O operation. On windows, fd must be a TCP socket (for UDP, it is difficult to simulate the behavior of epoll or kqueue with IOCP). 2, for windows only. fd can be a UDP socket, but users need to manually call WSARecvFrom, WSASendTo or other API to send overlapped I/O requests to IOCP, and then call the wait() method, when wait() returns successfully, it means that IOCP has completed the I/O operation. The specific usage is not detailed here. There are detailed comments in the code. It is recommended to refer directly to the source code of co::io_event, and the implementation of co::accept, co::connect, co::recvfrom, co::sendto on windows. #destructor ~io_event(); Removes previously registered I/O events from epoll or kqueue. #wait bool wait(uint32 ms=-1); This method waits for the I/O event on the socket. The parameter ms is the timeout in milliseconds. The default is -1 and never times out. This method blocks until the I/O event arrives, or times out or an error occurs. This method returns true on success, false on timeout or error. #Code Example int recv(sock_t fd, void* buf, int n, int ms) { const auto sched = xx::gSched; CHECK(sched) \u0026lt;\u0026lt; \u0026#34;must be called in coroutine..\u0026#34;; co::io_event ev(fd, ev_read); do { int r = (int) __sys_api(recv)(fd, buf, n, 0); if (r != -1) return r; if (errno == EWOULDBLOCK || errno == EAGAIN) { if (!ev.wait(ms)) return -1; } else if (errno != EINTR) { return -1; } } while (true); } "},{"id":23,"href":"/en/co/log/","title":"log","section":"Documents","content":"include: co/log.h.\n#Introduction co.log is a high-performance log library provided by coost. It supports two types of logs, level log and topic log (TLOG). It prints logs as follows:\nLOG \u0026lt;\u0026lt; \u0026#34;hello world\u0026#34; \u0026lt;\u0026lt; 23; // level log TLOG(\u0026#34;topic\u0026#34;) \u0026lt;\u0026lt; \u0026#34;hello\u0026#34; \u0026lt;\u0026lt; 23; // topic log #Level Log Level log is divided into 5 levels: debug, info, warning, error, fatal, and provides a series of macros to print logs of different levels.\nA fatal log will terminate the program, and co.log will also try to print the stack information before the program exits. Different levels of logs are written to the same file. It is usually used to print debugging information.\n#Topic Log Topic log (TLOG) has no level, but is categorized by topic.\nTopic logs are written into different files according to the topic, and is generally used to print business logs.\n#Performance co.log is internally implemented in an asynchronous manner. The logs are first written into the cache. After reaching a certain amount or exceeding a certain period of time, the background thread writes the file at one time. The performance is improved by about 20 to 150 times compared with glog on different platforms. The following table shows the test results of continuously printing 1 million info logs (about 50 bytes each) in a single thread on different platforms:\nplatform glog co.log win2012 HHD 1.6MB/s 180MB/s win10 SSD 3.7MB/s 560MB/s mac SSD 17MB/s 450MB/s linux SSD 54MB/s 1023MB/s #APIs #log::exit void exit(); Write logs in the cache to the file and exit the log thread. This function is automatically called by co.log when the program exits normally. It is safe to call this function multiple times. When co.log captures SIGINT, SIGTERM, SIGQUIT or other similar signals, this function will be called before the program exits. #log::set_write_cb void set_write_cb( const std::function\u0026lt;void(const void*, size_t)\u0026gt;\u0026amp; cb, int flags=0 ); void set_write_cb( const std::function\u0026lt;void(const char*, const void*, size_t)\u0026gt;\u0026amp; cb, int flags=0 ); By default, co.log writes logs to a local file. Users can set a callback to write logs to different destinations through this API. The parameter cb is the callback. In the first version (for level log), cb has 2 parameters, a pointer to the log buffer and its length. In the second version (for TLOG), cb has 3 parameters, the first is the topic, the last two parameters are the same as in the 1st version. The buffer may contain multiple logs. The parameter flags is new in v3.0, the default is 0, it can be a combination of the following options: log::log2local, also write logs to local file. #APIs removed in v3.0 log::init, removed in v3.0, starting from v3.0, we only need to call flag::parse(argc, argv) at the beginning of the main function.\nlog::set_single_write_cb, removed in v3.0.\nlog::close, removed in v3.0, use log::exit() instead.\n#Level Log #Basic usages DLOG LOG WLOG ELOG FLOG The above 5 macros are used to print 5 levels of logs respectively, they are thread safe.\nThese macros are actually references to fastream, so any type supported by fastream::operator\u0026lt;\u0026lt; can be printed.\nThese macros will automatically add a \u0026lsquo;\\n\u0026rsquo; at the end of each log, and users do not need to manually enter a newline character.\nThe first 4 types will only print the log when the FLG_min_log_level is not greater than the current log level. The user can set FLG_min_log_level to a larger value to disable low-level logs.\nPrint a fatal level log, which means that the program has a fatal error. coost will print the stack information of the current thread and terminate the program.\nExample\nDLOG \u0026lt;\u0026lt; \u0026#34;this is DEBUG log \u0026#34;\u0026lt;\u0026lt; 23; LOG \u0026lt;\u0026lt; \u0026#34;this is INFO log \u0026#34;\u0026lt;\u0026lt; 23; WLOG \u0026lt;\u0026lt; \u0026#34;this is WARNING log \u0026#34;\u0026lt;\u0026lt; 23; ELOG \u0026lt;\u0026lt; \u0026#34;this is ERROR log \u0026#34;\u0026lt;\u0026lt; 23; FLOG \u0026lt;\u0026lt; \u0026#34;this is FATAL log \u0026#34;\u0026lt;\u0026lt; 23; #Condition Log #define DLOG_IF(cond) if (cond) DLOG #define LOG_IF(cond) if (cond) LOG #define WLOG_IF(cond) if (cond) WLOG #define ELOG_IF(cond) if (cond) ELOG #define FLOG_IF(cond) if (cond) FLOG The above 5 macros accept a conditional parameter cond, and only print the log when cond is true. The parameter cond can be any expression whose value is of type bool. Since the condition is checked in the first place, even if the log of the corresponding level is disabled, these macros will ensure that the cond expression is executed. Example int s = socket(); DLOG_IF(s != -1) \u0026lt;\u0026lt; \u0026#34;create socket ok: \u0026#34;\u0026lt;\u0026lt; s; LOG_IF(s != -1) \u0026lt;\u0026lt; \u0026#34;create socket ok: \u0026#34;\u0026lt;\u0026lt; s; WLOG_IF(s == -1) \u0026lt;\u0026lt; \u0026#34;create socket ko: \u0026#34;\u0026lt;\u0026lt; s; ELOG_IF(s == -1) \u0026lt;\u0026lt; \u0026#34;create socket ko: \u0026#34;\u0026lt;\u0026lt; s; FLOG_IF(s == -1) \u0026lt;\u0026lt; \u0026#34;create socket ko: \u0026#34;\u0026lt;\u0026lt; s; #Print log every N entries #define DLOG_EVERY_N(n) _LOG_EVERY_N(n, DLOG) #define LOG_EVERY_N(n) _LOG_EVERY_N(n, LOG) #define WLOG_EVERY_N(n) _LOG_EVERY_N(n, WLOG) #define ELOG_EVERY_N(n) _LOG_EVERY_N(n, ELOG) The above macros print the log once every n entries.\nThe parameter n must be an integer greater than 0.\nThe first log will always be printed.\nThe program will terminate as soon as the fatal log is printed, so FLOG_EVERY_N is not provided.\nExample\n// Print every 32 items (1,33,65...) DLOG_EVERY_N(32) \u0026lt;\u0026lt; \u0026#34;this is DEBUG log \u0026#34;\u0026lt;\u0026lt; 23; LOG_EVERY_N(32) \u0026lt;\u0026lt; \u0026#34;this is INFO log \u0026#34;\u0026lt;\u0026lt; 23; WLOG_EVERY_N(32) \u0026lt;\u0026lt; \u0026#34;this is WARNING log \u0026#34;\u0026lt;\u0026lt; 23; ELOG_EVERY_N(32) \u0026lt;\u0026lt; \u0026#34;this is ERROR log \u0026#34;\u0026lt;\u0026lt; 23; #Print the first N logs #define DLOG_FIRST_N(n) _LOG_FIRST_N(n, DLOG) #define LOG_FIRST_N(n) _LOG_FIRST_N(n, LOG) #define WLOG_FIRST_N(n) _LOG_FIRST_N(n, WLOG) #define ELOG_FIRST_N(n) _LOG_FIRST_N(n, ELOG) The above macros print the first n logs.\nThe parameter n is an integer not less than 0 (no log will be printed when it is equal to 0). Generally, it should not exceed the maximum value of the int type.\nIn general, do not use complex expressions for the parameter n.\nThe program will terminate as soon as the fatal log is printed, so FLOG_FIRST_N is not provided.\nExample\n// print the first 10 logs DLOG_FIRST_N(10) \u0026lt;\u0026lt; \u0026#34;this is DEBUG log \u0026#34;\u0026lt;\u0026lt; 23; LOG_FIRST_N(10) \u0026lt;\u0026lt; \u0026#34;this is INFO log \u0026#34;\u0026lt;\u0026lt; 23; WLOG_FIRST_N(10) \u0026lt;\u0026lt; \u0026#34;this is WARNING log \u0026#34;\u0026lt;\u0026lt; 23; ELOG_FIRST_N(10) \u0026lt;\u0026lt; \u0026#34;this is ERROR log \u0026#34;\u0026lt;\u0026lt; 23; #TLOG #define TLOG(topic) #define TLOG_IF(topic, cond) if (cond) TLOG(topic) The TLOG macro takes a parameter topic, which is a C-style string, and must have a static lifetime.\nThe TLOG_IF macro prints the log only when cond is true.\nExample\nTLOG(\u0026#34;xx\u0026#34;) \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; TLOG_IF(\u0026#34;xx\u0026#34;, true) \u0026lt;\u0026lt; \u0026#34;hello \u0026#34; \u0026lt;\u0026lt; 23; #CHECK Assertion #define CHECK(cond) \\ if (!(cond)) _FLOG_STREAM \u0026lt;\u0026lt; \u0026#34;check failed: \u0026#34;#cond \u0026#34;!\u0026#34; #define CHECK_NOTNULL(p) \\ if ((p) == 0) _FLOG_STREAM \u0026lt;\u0026lt; \u0026#34;check failed: \u0026#34;#p\u0026#34; mustn\u0026#39;t be NULL! \u0026#34; #define CHECK_EQ(a, b) _CHECK_OP(a, b, ==) #define CHECK_NE(a, b) _CHECK_OP(a, b, !=) #define CHECK_GE(a, b) _CHECK_OP(a, b, \u0026gt;=) #define CHECK_LE(a, b) _CHECK_OP(a, b, \u0026lt;=) #define CHECK_GT(a, b) _CHECK_OP(a, b, \u0026gt;) #define CHECK_LT(a, b) _CHECK_OP(a, b, \u0026lt;) The above macros can be regarded as an enhanced version of assert, and they will not be cleared in DEBUG mode.\nThese macros are similar to FLOG and can print fatal level logs.\nCHECK asserts that the condition cond is true, and cond can be any expression with a value of type bool.\nCHECK_NOTNULL asserts that the pointer is not NULL.\nCHECK_EQ asserts a == b.\nCHECK_NE asserts a != b.\nCHECK_GE asserts a \u0026gt;= b.\nCHECK_LE asserts a \u0026lt;= b.\nCHECK_GT asserts a \u0026gt; b.\nCHECK_LT asserts a \u0026lt; b.\nIt is generally recommended to use CHECK_XX(a, b) first, they provide more information than CHECK(cond), and will print out the values of parameters a and b.\nTypes not supported by fastream::operator\u0026lt;\u0026lt;, such as iterator type of STL containers, cannot use the CHECK_XX(a, b) macros.\nWhen the assertion fails, co.log calls log::exit() at first, then prints the stack information of the current thread, and then exits the program.\nExample\nint s = socket(); CHECK(s != -1); CHECK(s != -1) \u0026lt;\u0026lt; \u0026#34;create socket failed\u0026#34;; CHECK_NE(s, -1) \u0026lt;\u0026lt; \u0026#34;create socket failed\u0026#34;; // s != -1 CHECK_GE(s, 0) \u0026lt;\u0026lt; \u0026#34;create socket failed\u0026#34;; // s \u0026gt;= 0 CHECK_GT(s, -1) \u0026lt;\u0026lt; \u0026#34;create socket failed\u0026#34;; // s \u0026gt; -1 std::map\u0026lt;int, int\u0026gt; m; auto it = m.find(3); CHECK(it != m.end()); // Cannot use CHECK_NE(it, m.end()), the compiler will report an error #Stack trace co.log will print the stack information when CHECK assertion failed, or an abnormal signal like SIGSEGV was caught. See details below:\n(https://asciinema.org/a/435894)\nTo get the stack trace, you should compile with debug symbols (compile with -g for gcc, etc). And on linux and macosx, libbacktrace is required, make sure you have installed it on your system. On linux, libbacktrace may have been installed within gcc. You may find it in a directory like /usr/lib/gcc/x86_64-linux-gnu/9. Otherwise, you can install it by yourself as follow:\ngit clone https://github.com/ianlancetaylor/libbacktrace.git cd libbacktrace-master ./configure make -j8 sudo make install #Configuration co.log uses co.flag to define config items. The flags defined inside co.log are listed below. These config items are valid for both level log and TLOG unless otherwise specified.\n#log_dir Specify the log directory. The default is the logs directory under the current directory. If it does not exist, it will be created automatically. log_dir can be an absolute path or a relative path, and the path separator can be either \u0026lsquo;/\u0026rsquo; or \u0026lsquo;\\\u0026rsquo;. It is generally recommended to use \u0026lsquo;/\u0026rsquo;. When the program starts, make sure that the current user has sufficient permissions, otherwise the creation of the log directory may fail. #log_file_name Specify the log file name (without path), the default is empty, use the program name (.exe at the end will be removed), for example, the log file name corresponding to program xx or xx.exe is xx.log. If the log file name does not end with .log, co/log automatically adds .log to the end of it. #min_log_level For level log only. Specify the minimum level of logs to be printed, which can be used to disable low-level logs, the default is 0, and all levels of logs are printed. #max_log_size Specify the maximum size of a single log, the default is 4k. A log will be truncated if its size is larger than this value. #max_log_file_size Specify the maximum size of a log file. The default is 256M. If this size is exceeded, a new log file will be created, and the old log file will be renamed. #max_log_file_num Specify the maximum number of log files. The default is 8. If this value is exceeded, old log files will be deleted. #max_log_buffer_size Specify the maximum size of the log cache. The default is 32M. If this value is exceeded, about half of the logs will be lost. #log_flush_ms The time interval for the background thread to flush the log cache to the file, in milliseconds. #log_daily Generate log files by day, the default is false. #cout Terminal log switch, the default is false. If true, logs will also be printed to the console. #Log file #Log file name co.log will write all levels of logs into the same file. By default, the program name is used as the log file name. For example, the log file of process xx is xx.log. When the log file reaches the maximum size (FLG_max_log_file_size), co.log will rename the log file and generate a new file. The log directory may contain the following files:\nxx.log xx_0523_16_12_54.970.log xx_0523_16_13_12.921.log xx_0523_16_15_05.264.log xx.log is always the latest log file. When the number of files exceeds FLG_max_log_file_num, co.log will remove the oldest log file.\nfatal logs will be additionally recorded in the xx.fatal file, co.log will not rename or delete the fatal log file.\n#Log format I0514 11:15:30.123 1045 xx.cc:11] hello world D0514 11:15:30.123 1045 xx.cc:12] hello world W0514 11:15:30.123 1045 xx.cc:13] hello world E0514 11:15:30.123 1045 xx.cc:14] hello world F0514 11:15:30.123 1045 xx.cc:15] hello world 0514 11:15:30.123 1045 xx.cc:11] hello world Level log from left to right, it is the level, time (month to millisecond), thread id, file and line number, and the log content. The first letter of level log is the log level, I for info, D for debug, W for warning, E for error, F for fatal. Topic log has no level, others are the same as level log (The last log in the above example is a topic log). #View logs On linux or mac, grep, tail and other commands can be used to view the logs.\ngrep ^E xx.log tail -F xx.log tail -F xx.log | grep ^E The first line uses grep to filter out the error logs in the file, ^E means starts with the letter E. The second line uses the tail -F command to dynamically track the log file, here we should use the uppercase F, because xx.log may be renamed, and then generate a new xx.log file, -F make sure to follow the latest file by the name. In line 3, use tail -F in conjunction with grep to dynamically track the error logs in the log file. #Build and run the test program xmake -b log # build log or log.exe xmake r log # run log or log.exe xmake r log -cout # also log to terminal xmake r log -min_log_level=1 # 0-4: debug,info,warning,error,fatal xmake r log -perf # performance test Run xmake -b log in the root directory of coost to compile test/log.cc, and a binary program named log or log.exe will be generated. "},{"id":24,"href":"/en/co/other/stl/","title":"STL","section":"others","content":"include: co/stl.h.\n#Common containers In the table below, the containers in coost on the left are equivalent to the corresponding std versions on the right, only the internal memory allocators are different.\ncoost std co::deque std::deque co::list std::list co::map std::map co::set std::set co::multimap std::multimap co::multiset std::multiset co::hash_map std::unordered_map co::hash_set std::unordered_set When the key is of const char* type (C-style string), co::map, co::set, co::multimap, co::multiset, co:: hash_map, co::hash_set will compare the key and calculate the hash value of the key according to the content of the string. #co::lru_map template\u0026lt;typename K, typename V\u0026gt; class lru_map; co::lru_map is a map implemented based on the LRU (least recently used) strategy. When the number of elements in the map reaches the upper limit, the least recently used data will be replaced first. It is implemented based on co::hash_map and co::list, and the internal elements are unordered.\n#constructor 1. lru_map(); 2. explicit lru_map(size_t capacity); 1, the default constructor, uses 1024 as the maximum capacity. 2, use the parameter capacity as the maximum capacity. #begin iterator begin() const; Returns an iterator refer to the first element. When lru_map is empty, the return value is equal to end(). #clear void clear(); Clear the elements in lru_map, the size will become 0, and the capacity will remain unchanged. #empty bool empty() const; Determine whether lru_map is empty. #end iterator end() const; Returns an iterator refer to the next position of the last element. #erase void erase(iterator it); void erase(const key_type\u0026amp; key); Remove element by iterator or key. #find iterator find(const key_type\u0026amp; key) Find the element corresponding to key. #insert template\u0026lt;typename Key, typename Val\u0026gt; void insert(Key\u0026amp;\u0026amp; key, Val\u0026amp;\u0026amp; value); Inserts an element, only if the key does not exist, a new element will be inserted. If the key already exists, nothing will be done. If the number of elements has reached the maximum capacity, the least recently used element will be deleted. #size size_t size() const; Returns the number of elements in lru_map. #swap void swap(lru_map\u0026amp; x) noexcept; void swap(lru_map\u0026amp;\u0026amp; x) noexcept; Swap the contents of two lru_maps. #Code example co::lru_map\u0026lt;int, int\u0026gt; m(128); // capacity: 128 auto it = m.find(1); if (it == m.end()) { m.insert(1, 23); } else { it-\u0026gt;second = 23; } m.erase(it); // erase by iterator m.erase(1); // erase by key m.clear(); // clear the map #co::vector std::vector\u0026lt;bool\u0026gt; in the C++ standard library is an unwise design. For this reason, coost implemented co::vector separately.\n#constructor 1. constexpr vector() noexcept; 2. explicit vector(size_t cap); 3. vector(const vector\u0026amp; x); 4. vector(vector\u0026amp;\u0026amp; x) noexcept; 5. vector(std::initializer_list\u0026lt;T\u0026gt; x); 6. vector(T* p, size_t n); 7. template\u0026lt;typename It\u0026gt; vector(It beg, It end); 8. template\u0026lt;typename X\u0026gt; vector(size_t n, X\u0026amp;\u0026amp; x); 1, the default constructor, constructs an empty vector with size and capacity both being 0. 2, Construct an empty vector with capacity as cap. 3, copy constructor. 4, Move constructor, after construction, x becomes an empty object. 5, Construct vector with a initialization list, T is the type of elements in the vector. 6, Construct vector from an array, p points to the first element of the array, n is the length of the array. 7, Construct vector with elements in range [beg, end). 8, There are two cases: when X is not int or the element type T is int, the vector is initialized with n values of x; when X is int and the element type T is not int, the vector is initialized to n default values of type T. The behavior of constructor 2 is different from the corresponding version in std::vector. To construct a vector of n default values, you can use constructor 8 (the second parameter passes 0):\nco::vector\u0026lt;fastring\u0026gt; v(32, 0); Example co::vector\u0026lt;int\u0026gt; a(32); // size: 0, capacity: 32 co::vector\u0026lt;int\u0026gt; b = { 1, 2 }; // [1,2] co::vector\u0026lt;int\u0026gt; v(8, 0); // contains 8 zeros co::vector\u0026lt;fastring\u0026gt; s(8, 0); // contains 8 empty fastring objects #destructor ~vector(); Release the memory, and the vector becomes an empty object after destruction. #operator= 1. vector\u0026amp; operator=(const vector\u0026amp; x); 2. vector\u0026amp; operator=(vector\u0026amp;\u0026amp; x); 3. vector\u0026amp; operator=(std::initializer_list\u0026lt;T\u0026gt; x); Assignment operations.\nExample\nco::vector\u0026lt;int\u0026gt; v; v = { 1, 2, 3 }; co::vector\u0026lt;int\u0026gt; x; x = v; x = std::move(v); #———————————— #back T\u0026amp; back(); const T\u0026amp; back() const; Returns a reference to the last element in vector. If vector is empty, calling this method results in undefined behavior. #front T\u0026amp; front(); const T\u0026amp; front() const; Returns a reference to the first element of the vector. If vector is empty, calling this method results in undefined behavior. #operator[] T\u0026amp; operator[](size_t n); const T\u0026amp; operator[](size_t n) const; Returns a reference to the nth element of vector. If n is outside a reasonable range, calling this method results in undefined behavior. #———————————— #begin iterator begin() const noexcept; Returns an iterator pointing to the first element. #end iterator end() const noexcept; Returns the end iterator. #———————————— #capacity size_t capacity() const noexcept; Returns the capacity of the vector. #data T* data() const noexcept; Returns a pointer to the internal elements of the vector. #empty bool empty() const noexcept; Determine whether the vector is empty. #size size_t size() const noexcept; Returns the number of elements in the vector. #———————————— #clear void clear(); Empty the vector, the size becomes 0, and the capacity remains unchanged. #reserve void reserve(size_t n); Adjust the capacity of the vector to ensure that the capacity is at least n. When n is less than the original capacity, keep the capacity unchanged. #reset void reset(); Destroy all elements in the vector and release the memory . #resize void resize(size_t n); Resize vector to n. When n is larger than the original size, fill the extended part with the default value. #swap void swap(vector\u0026amp; x) noexcept; void swap(vector\u0026amp;\u0026amp; x) noexcept; Swaps two vectors. #———————————— #append 1. void append(const T\u0026amp; x); 2. void append(T\u0026amp;\u0026amp; x); 3. void append(size_t n, const T\u0026amp; x); 4. void append(const T* p, size_t n); 5. void append(const vector\u0026amp; x); 6. void append(vector\u0026amp;\u0026amp; x); 7. template\u0026lt;typename It\u0026gt; void append(It beg, It end); Add elements to the end of the vector.\n1-2, append a single element.\n3, append n elements x.\n4, append an array, n is length of the array.\n5-6, append all elements in vector x.\n7, append elements in range [beg, end).\nExample\nint a[4] = { 1, 2, 3, 4 }; co::vector\u0026lt;int\u0026gt; v; v. append(7); v. append(v); v. append(a, 4); #emplace_back template\u0026lt;typename ... X\u0026gt; void emplace_back(X\u0026amp;\u0026amp; ... x); Insert a new element constructed with the arguments x... at the end of the vector.\nExample\nco::vector\u0026lt;fastring\u0026gt; v; v. emplace_back(4, \u0026#39;x\u0026#39;); // append(\u0026#34;xxxx\u0026#34;) #push_back void push_back(const T\u0026amp; x); void push_back(T\u0026amp;\u0026amp; x); Appends the element x to the end of the vector. #pop_back T pop_back(); Pop and return the last element of the vector. If vector is empty, calling this method results in undefined behavior. #remove void remove(size_t n); Remove the nth element and moves the last element to the position of the deleted element.\nExample\nco::vector\u0026lt;int\u0026gt; v = { 1, 2, 3, 4 }; v. remove(1); // v -\u0026gt; [1, 4, 3] v. remove(2); // v -\u0026gt; [1, 4] #remove_back void remove_back(); Remove the last element of the vector, and do nothing if the vector is empty. "},{"id":25,"href":"/en/co/net/tcp/","title":"TCP","section":"Network Programming","content":"include: co/tcp.h.\n#tcp::Connection tcp::Connection is a simple encapsulation of TCP connection, it is designed for TCP server. When SSL is enabled in a TCP server, tcp::Connection will transfer data by SSL.\n#Connection::Connection Connection(int sock); Connection(void* ssl); Connection(Connection\u0026amp;\u0026amp; c); The constructor, Connection is created by tcp::Server, users do not need to create it manually. The first version constructs a normal TCP connection, the second version constructs a TCP connection that support SSL, and the third is a move constructor. #Connection::~Connection Connection::~Connection(); Destructor, call close() to close the connection. #Connection::close int close(int ms = 0); Close the connection. When ms \u0026gt; 0, close the connection after a certain delay. Since v2.0.1, this method can be called anywhere(in coroutine or non-coroutine). #Connection::recv int recv(void* buf, int n, int ms=-1); Receive data, similar to co::recv. This method must be called in coroutine. Return \u0026gt;0 on success, \u0026lt;0 on timeout or any error, and 0 will be returned if the peer closed the connection. #Connection::recvn int recvn(void* buf, int n, int ms=-1); Receive data of specified length, similar to co::recvn. Return n on success, \u0026lt;0 on timeout or any error, and 0 will be returned if the peer closed the connection. #Connection::reset int reset(int ms = 0) Reset the TCP connection, unlike close(), it will not enter the TIME_WAIT state. When ms \u0026gt; 0, the connection will be reset after a certain delay. This method must be called in the I/O thread (usually a coroutine that performs the I/O operations). #Connection::send int send(const void* buf, int n, int ms=-1); Send data, similar to co::send(). return n on success, \u0026lt;= 0 on timeout or error. #Connection::socket int socket() const; Return the internal socket descriptor, -1 will be returned if the connection was closed. #Connection::strerror const char* strerror() const; When an error occurs in any method of Connection, users can call this method to get the error message. #tcp::Server tcp::Server is a TCP server based on coroutine. It has the following features:\nSupport IPv4 and IPv6. Support SSL (openssl is required). One coroutine for each client connection. #Server::Server Server(); The constructor, initialization. #Server::conn_num uint32 conn_num() const; Returns number of client connections. #Server::on_connection Server\u0026amp; on_connection(std::function\u0026lt;void(Connection)\u0026gt;\u0026amp;\u0026amp; f); Server\u0026amp; on_connection(const std::function\u0026lt;void(Connection)\u0026gt;\u0026amp; f); template\u0026lt;typename T\u0026gt; Server\u0026amp; on_connection(void (T::*f)(Connection), T* o); Set a callback for handling connections.\nIn the first 2 versions, the parameter f is a function of type void f(Connection), or a function object of type std::function\u0026lt;void(Connection)\u0026gt;.\nIn the third version, the parameter f is a method in class T, and the parameter o is a pointer to type T.\nSince v2.0.2, the parameter of f is an object of tcp::Connection, rather than a pointer, and users do not need to delete it any more.\nWhen the server receives a connection, it will create a new coroutine and call the callback set by this method in the coroutine to handle the connection.\nExample\nvoid f(tcp::Connection conn); tcp::Server s; s.on_connection(f); void f(tcp::Connection conn) { while (true) { conn.recv(...); process(...); conn.send(...); } conn.close(); } #Server::on_exit Server\u0026amp; on_exit(std::function\u0026lt;void()\u0026gt;\u0026amp;\u0026amp; cb); Set a callback which will be called when the server exits. #Server::start void start(const char* ip, int port, const char* key=0, const char* ca=0); Start the TCP server, this method will not block the current thread.\nThe parameter ip is the server ip, which can be an IPv4 or IPv6 address, and the parameter port is the server port.\nThe parameter key is path of a PEM file which stores the SSL private key, and the parameter ca is path of a PEM file which stores the SSL certificate. They are NULL by default, and SSL is disabled.\nStarting from v3.0, the server no longer depends on the tcp::Server object after it is started.\nExample\nvoid f(tcp::Connection conn); tcp::Server().on_connection(f).start(\u0026#34;0.0.0.0\u0026#34;, 7788); #Server::exit void exit(); Added since v2.0.2. Exit the TCP server, close the listening socket, and no longer receive new connections. This method will not close the connections that has been established before. If you need to close the previously established connections after the server exits, you can refer to test/tcp2.cc or implementations of http::Server and rpc::Server in co. #tcp::Client tcp::Client is a TCP client based on coroutine. It has following features:\nSupport IPv4 and IPv6. Support SSL (openssl is required). One client corresponds to one connection. It must be used in coroutine. It is not coroutine-safe, and it cannot be used by multiple coroutines at the same time. #Client::Client Client(const char* ip, int port, bool use_ssl=false); Client(const Client\u0026amp; c); Constructor. The parameter ip is the ip of the server, which can be a domain name, or an IPv4 or IPv6 address; the parameter port is the server port; the parameter use_ssl indicates whether to enable SSL transmission, the default is false. The second version is the copy constructor, value of ip, port and use_ssl will be copied from another client. The connection is not established in the constructor. It is generally recommended to check whether the connection has been established before calling recv, send. If not, call connect() to establish the connection. It is easy to support auto-reconnection in this way. #Client::~Client Client::~Client(); Destructor, call the disconnect() method to close the connection. #Client::close void close(); Close the connection, the same as disconnect(). #Client::connect bool connect(int ms); Establish a connection, the parameter ms is the timeout period in milliseconds. This method must be called in coroutine. This method returns true on success, otherwise it returns false. When it fails, users can call strerror() to get the error message. #Client::connected bool connected() const; Determine whether the connection has been established. #Client::disconnect void disconnect(); Since v2.0.1, it can be called anywhere(in coroutine or non-coroutine). It is safe to call this method multiple times, and it will be called automatically in the destructor. #Client::recv int recv(void* buf, int n, int ms=-1); Receive data, similar to co::recv(). This method must be called in coroutine. Return \u0026gt;0 on success, \u0026lt;0 on timeout or any error, and 0 will be returned if the peer closed the connection. #Client::recvn int recvn(void* buf, int n, int ms=-1); Receive data of specified length, similar to co::recvn(). This method must be called in coroutine. Return n on success, \u0026lt;0 on timeout or any error, and 0 will be returned if the peer closed the connection. #Client::send int send(const void* buf, int n, int ms=-1); Send data, similar to co::send(). This method must be called in coroutine. return n on success, \u0026lt;=0 on timeout or error. #Client::socket int socket() const; Return the internal socket descriptor. When the connection is not established or the connection has been closed, the return value is -1. #Client::strerror const char* strerror() const; When an error occurs in any method of tcp::Client, users can call this method to get the error message. #TCP server example void on_connection(tcp::Connection conn) { char buf[8] = { 0 }; while (true) { int r = conn.recv(buf, 8); if (r == 0) { /* client close the connection */ conn.close(); break; } else if (r \u0026lt; 0) { /* error */ conn.reset(3000); break; } else { LOG \u0026lt;\u0026lt; \u0026#34;server recv \u0026#34; \u0026lt;\u0026lt; fastring(buf, r); LOG \u0026lt;\u0026lt; \u0026#34;server send pong\u0026#34;; r = conn.send(\u0026#34;pong\u0026#34;, 4); if (r \u0026lt;= 0) { LOG \u0026lt;\u0026lt; \u0026#34;server send error: \u0026#34; \u0026lt;\u0026lt; conn.strerror(); conn.reset(3000); break; } } } } tcp::Server s; s.on_connection(on_connection); s.start(\u0026#34;0.0.0.0\u0026#34;, 7788); // no ssl s.start(\u0026#34;0.0.0.0\u0026#34;, 7788, \u0026#34;privkey.pem\u0026#34;, \u0026#34;certificate.pem\u0026#34;); // use ssl The above example implements a simple ping-pong server, when it receives a ping sent by the client, it will reply with a pong. #TCP client example bool use_ssl = false; std::unique_ptr\u0026lt;tcp::Client\u0026gt; proto; co::pool pool( []() {return (void*) new tcp::Client(*proto); }, [](void* p) {delete (tcp::Client*) p;} ); void client_fun() { co::pool_guard\u0026lt;tcp::Client\u0026gt; c(pool); if (!c-\u0026gt;connect(3000)) { LOG \u0026lt;\u0026lt; \u0026#34;connect failed: \u0026#34;\u0026lt;\u0026lt; c-\u0026gt;strerror(); return; } char buf[8] = {0 }; while (true) { LOG \u0026lt;\u0026lt; \u0026#34;client send ping\u0026#34;; int r = c-\u0026gt;send(\u0026#34;ping\u0026#34;, 4); if (r \u0026lt;= 0) { LOG \u0026lt;\u0026lt; \u0026#34;client send error: \u0026#34;\u0026lt;\u0026lt; c-\u0026gt;strerror(); break; } r = c-\u0026gt;recv(buf, 8); if (r \u0026lt; 0) { LOG \u0026lt;\u0026lt; \u0026#34;client recv error: \u0026#34;\u0026lt;\u0026lt; c-\u0026gt;strerror(); break; } else if (r == 0) { LOG \u0026lt;\u0026lt; \u0026#34;server close the connection\u0026#34;; break; } else { LOG \u0026lt;\u0026lt; \u0026#34;client recv \u0026#34;\u0026lt;\u0026lt; fastring(buf, r) \u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; co::sleep(3000); } } } proto.reset(new tcp::Client(\u0026#34;127.0.0.1\u0026#34;, 7788, use_ssl)); for (int i = 0; i \u0026lt;8; ++i) { go(client_fun); } In the above example, we use co::pool to cache client connections, and different coroutines can share connections in the pool. "},{"id":26,"href":"/en/co/net/http/","title":"HTTP","section":"Network Programming","content":"include: co/http.h.\n#http::Client http::Client is a coroutine-based HTTP client, which is implemented based on libcurl.\n#Client::Client explicit Client(const char* serv_url); Constructor, the parameter serv_url is the url address of the server, and its form is protocol://host:port, the following server urls are all ok: \u0026ldquo;github.com\u0026rdquo; \u0026ldquo;https://github.com\u0026rdquo; \u0026ldquo;http://127.0.0.1:7788\u0026rdquo; \u0026ldquo;http://[::1]:8888\u0026rdquo; Connection is not established in the constructor. #Client::~Client Client::~Client(); Destructor, close the connection, and release libcurl related resources. #Client::add_header void add_header(const char* key, const char* val); void add_header(const char* key, int val); Add a HTTP header. Users can use this method to add headers before performing HTTP requests, and the added headers will be present in all subsequent requests. In the second version, the parameter val is an integer, which is automatically converted to string internally. #Client::body const fastring\u0026amp; body() const; Get the response body of the current HTTP request. #Client::close void close(); Close the HTTP connection, must be called in coroutine. Once this method is called, the http::Client object can no longer be used until you reset the server url by calling reset(). #Client::del void del(const char* url, const char* s, size_t n); void del(const char* url, const char* s); void del(const char* url); HTTP DELETE request, must be called in coroutine. The parameter url must be a string beginning with '/'. The first two versions are suitable for DELETE requests with a body, and parameter s is a pointer to the body data. The third version is for DELETE requests without body. #Client::easy_handle void* easy_handle() const; Return the easy handle of libcurl. #Client::get void get(const char* url); HTTP GET request, must be called in coroutine. The parameter url must be a string beginning with /. #Client::head void head(const char* url); HTTP HEAD request, must be called in coroutine. The parameter url must be a string beginning with /. #Client::header const char* header(const char* key); const fastring\u0026amp; header() const; The first version gets value of a HTTP header. If the header does not exist, an empty string is returned.\nThe second version gets the entire HTTP header part (include the start line).\nExample\nhttp::Client c(\u0026#34;xx.com\u0026#34;); c.get(\u0026#34;/\u0026#34;); auto s = c.header(\u0026#34;Content-Length\u0026#34;); #Client::perform void perform(); Perform a HTTP request, get, post and other methods are actually implemented based on this method.\nUsers generally don\u0026rsquo;t need to call this method. Only when the get, post and other methods provided by http::Client can\u0026rsquo;t meet their needs, should they consider using this method to customize HTTP requests.\nExample\nvoid Client::post(const char* url, const char* data, size_t size) { curl_easy_setopt(_ctx-\u0026gt;easy, CURLOPT_POST, 1L); curl_easy_setopt(_ctx-\u0026gt;easy, CURLOPT_URL, make_url(url)); curl_easy_setopt(_ctx-\u0026gt;easy, CURLOPT_POSTFIELDS, data); curl_easy_setopt(_ctx-\u0026gt;easy, CURLOPT_POSTFIELDSIZE, (long)size); this-\u0026gt;perform(); } #Client::post void post(const char* url, const char* s, size_t n); void post(const char* url, const char* s); HTTP POST request, must be called in coroutine. The parameter url must be a string beginning with /. #Client::put void put(const char* url, const char* path); HTTP PUT request, used to upload a file, must be called in coroutine. The parameter url must be a string beginning with /. The parameter path is path of the file to be uploaded. #Client::remove_header void remove_header(const char* key); The headers added by add_header() method will apply to all subsequent HTTP requests. If users do not want a header to appear in subsequent requests, this method can be called to remove the header. #Client::reset void reset(const char* serv_url); Reset the server url. #Client::response_code int response_code() const; Get the response code of the current HTTP request. Normally, the return value is a value between 100 and 511. If the HTTP request is not sent due to network error or other reasons, or no response from the server was received within the timeout period, this method returns 0, and strerror() can be called to get the error message. #Client::status int status() const; Equal to response_code(). #Client::strerror const char* strerror() const; Get the error message of the current HTTP request. #Example void f() { http::Client c(\u0026#34;https://github.com\u0026#34;); int r; c.get(\u0026#34;/\u0026#34;); r = c.status(); LOG \u0026lt;\u0026lt; \u0026#34;staus: \u0026#34; \u0026lt;\u0026lt; r; LOG_IF(r == 0) \u0026lt;\u0026lt; \u0026#34;error: \u0026#34; \u0026lt;\u0026lt; c.strerror(); LOG \u0026lt;\u0026lt; \u0026#34;body size: \u0026#34; \u0026lt;\u0026lt; c.body().size(); LOG \u0026lt;\u0026lt; c.header(); c.get(\u0026#34;/idealvin/co\u0026#34;); LOG \u0026lt;\u0026lt; \u0026#34;body size: \u0026#34; \u0026lt;\u0026lt; c.body().size(); LOG \u0026lt;\u0026lt; \u0026#34;Content-Length: \u0026#34; \u0026lt;\u0026lt; c.header(\u0026#34;Content-Length\u0026#34;); LOG \u0026lt;\u0026lt; c.header(); c.close(); } go(f); #http::Req http::Req is an encapsulation of HTTP request, it is used in http::Server.\n#Req::Req Req() = default; Default constructor. #Req::body const char* body() const; Get the body data in the HTTP request. It returns a pointer, not null-terminated. Users must call body_size() to get its length. #Req::body_size size_t body_size() const; Returns length of the HTTP request body. #Req::header const char* header(const char* key) const; Get value of a HTTP header. If the header does not exist, an empty string is returned. #Req::is_method_xxx bool is_method_get() const; bool is_method_head() const; bool is_method_post() const; bool is_method_put() const; bool is_method_delete() const; bool is_method_options() const; Determine the method type of the HTTP request. #Req::method Method method() const; Returns the HTTP request method, the return value is one of kGet, kHead, kPost, kPut, kDelete, kOptions. #Req::url const fastring\u0026amp; url() const; Returns a reference to the url in the HTTP request. This value is part of the start line of the HTTP request. #Req::version Version version() const; Returns version in the HTTP request. The return value is one of http::kHTTP10 or http::kHTTP11. Currently, HTTP/2.0 is not supported. #http::Res http::Res class is the encapsulation of HTTP response, it is used in http::Server.\n#Res::Res Res(); Default constructor. #Res::add_header void add_header(const char* key, const char* val); void add_header(const char* key, int val); Add a HTTP header. #Res::set_body void set_body(const void* s, size_t n); void set_body(const char* s); void set_body(const fastring\u0026amp; s); Set the body part of the HTTP response. The parameter s is the body data, and the parameter n is the length of s. In the second version, s ends with '\\0'. #Res::set_status void set_status(int status); Set the HTTP response code, this value is generally between 100 and 511. #http::Server http::Server is a coroutine-based HTTP server. It supports HTTPS. To use HTTPS, you need to install openssl first.\n#Server::Server Server(); The default constructor, users don\u0026rsquo;t need to care. #Server::on_req Server\u0026amp; on_req(std::function\u0026lt;void(const Req\u0026amp;, Res\u0026amp;)\u0026gt;\u0026amp;\u0026amp; f); Server\u0026amp; on_req(const std::function\u0026lt;void(const Req\u0026amp;, Res\u0026amp;)\u0026gt;\u0026amp; f) template\u0026lt;typename T\u0026gt; Server\u0026amp; on_req(void (T::*f)(const Req\u0026amp;, Res\u0026amp;), T* o); Set a callback for handling a HTTP request. In the third version, the parameter f is a method in class T, and the parameter o is a pointer to type T. When the server receives a HTTP request, it will call the callback set by this method to handle the request. #Server::start void start(const char* ip=\u0026#34;0.0.0.0\u0026#34;, int port=80); void start(const char* ip, int port, const char* key, const char* ca); Start the HTTP server, this method will not block the current thread. The parameter ip is the server ip, which can be an IPv4 or IPv6 address, and the parameter port is the server port. The parameter key is the path of a PEM file which stores the SSL private key, and the parameter ca is the path of a PEM file which stores the SSL certificate. If key or ca is NULL or empty string, SSL will be disabled. Starting from v3.0, the server no longer depends on the http::Server object after startup. #Server::exit void exit(); Added since v2.0.2. Exit the HTTP server, close the listening socket, and no longer receive new connections. Since v3.0, after the HTTP server exits, previously established connections will be reset in the future. #Example void cb(const http::Req\u0026amp; req, http::Res\u0026amp; res) { if (req.is_method_get()) { if (req.url() == \u0026#34;/hello\u0026#34;) { res.set_status(200); res.set_body(\u0026#34;hello world\u0026#34;); } else { res.set_status(404); } } else { res.set_status(405); // method not allowed } } // http http::Server().on_req(cb).start(\u0026#34;0.0.0.0\u0026#34;, 80); // https http::Server().on_req(cb).start( \u0026#34;0.0.0.0\u0026#34;, 443, \u0026#34;privkey.pem\u0026#34;, \u0026#34;certificate.pem\u0026#34; ); #Static web server void easy(const char* root_dir=\u0026#34;.\u0026#34;, const char* ip=\u0026#34;0.0.0.0\u0026#34;, int port=80); void easy(const char* root_dir, const char* ip, int port, const char* key, const char* ca); Start a static web server, the parameter root_dir is the root directory of the web server.\nThe parameter ip can be an IPv4 or IPv6 address.\nThe second version supports HTTPS, the parameter key is the SSL private key, the parameter ca is the SSL certificate, and both key and ca are files in pem format. When key or ca is NULL or an empty string, HTTPS is disabled.\nThis method will block the current thread.\nExample\n#include \u0026#34;co/flag.h\u0026#34; #include \u0026#34;co/http.h\u0026#34; DEF_string(d, \u0026#34;.\u0026#34;, \u0026#34;root dir\u0026#34;); // root dir of web server int main(int argc, char** argv) { flag::parse(argc, argv); so::easy(FLG_d.c_str()); // mum never have to worry again return 0; } #Config items Coost uses co.flag to define config items for HTTP.\n#http_conn_timeout DEF_uint32(http_conn_timeout, 3000, \u0026#34;#2 connect timeout in ms for http client\u0026#34;); Connect timeout in milliseconds for http::Client. #http_timeout DEF_uint32(http_timeout, 3000, \u0026#34;#2 send or recv timeout in ms for http client\u0026#34;); Receive and send timeout (libcurl does not distinguish between receive and send timeout) in milliseconds for http::Client. #http_recv_timeout DEF_uint32(http_recv_timeout, 3000, \u0026#34;#2 recv timeout in ms for http server\u0026#34;); Recv timeout in milliseconds for http::Server. #http_send_timeout DEF_uint32(http_send_timeout, 3000, \u0026#34;#2 send timeout in ms for http server\u0026#34;); Send timeout in milliseconds for http::Server. #http_conn_idle_sec DEF_uint32(http_conn_idle_sec, 180, \u0026#34;#2 http server may close the con...\u0026#34;); Timeout in seconds for http::Server to keep an idle connection. If the server does not receive data from the client within this time, it may close the connection. #http_log DEF_bool(http_log, true, \u0026#34;#2 enable http server log if true\u0026#34;); For http::Server, whether to print logs, the default is true. The log in http::Server will print the header part of HTTP request and response. #http_max_idle_conn DEF_uint32(http_max_idle_conn, 128, \u0026#34;#2 max idle connections for http server\u0026#34;); For http::Server, maximum number of idle connections. When this number is exceeded, some idle connections will be closed. #http_max_body_size DEF_uint32(http_max_body_size, 8 \u0026lt;\u0026lt; 20, \u0026#34;#2 max size of http body, default: 8M\u0026#34;); The maximum body length supported by http::Server, the default is 8M. #http_max_header_size DEF_uint32(http_max_header_size, 4096, \u0026#34;#2 max size of http header\u0026#34;); The maximum header (the entire HTTP header) length supported by http::Server, the default is 4k. "},{"id":27,"href":"/en/co/concurrency/coroutine/mutex/","title":"Mutex Lock","section":"Coroutine","content":"include: co/co.h.\n#co::mutex Starting from v3.0.1, co::mutex can be used in coroutines and non-coroutines. #constructor 1. mutex(); 2. mutex(mutex\u0026amp;\u0026amp; m); 3. mutex(const mutex\u0026amp; m); 1, default constructor. 2, move constructor. 3, copy constructor, only increases the internal reference count by 1. #lock void lock() const; Acquire the lock, will block until the lock is acquired. #try_lock bool try_lock() const; Acquire the lock, will not block, return true when the lock is successfully acquired, otherwise return false. #unlock void unlock() const; Release the lock, usually called by the coroutine or thread that previously obtained the lock. #co::mutex_guard #constructor explicit mutex_guard(co::mutex\u0026amp; m); explicit mutex_guard(co::mutex* m); Call m.lock() to acquire the lock, the parameter m is a reference or pointer of the co::mutex class. #destructor ~mutex_guard(); Release the lock acquired in the constructor. #Code Example #include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; co::mutex g_m; int g_v = 0; void f() { co::mutex_guard g(g_m); ++g_v; } int main(int argc, char** argv) { flag::parse(argc, argv); go(f); go(f); f(); f(); co::sleep(100); co::print(\u0026#34;g_v: \u0026#34;, g_v); return 0; } "},{"id":28,"href":"/en/co/other/str/","title":"String Utility","section":"others","content":"include: co/str.h.\n#String operations #str::cat template \u0026lt;typename ...X\u0026gt; inline fastring cat(X\u0026amp;\u0026amp; ... x); Added in v2.0.3. Concatenate any number of elements to make a string.\n示例\nstr::cat(\u0026#34;hello\u0026#34;, \u0026#39; \u0026#39;, 23); // -\u0026gt; \u0026#34;hello 23\u0026#34; str::cat(\u0026#34;xx\u0026#34;, 3.14, \u0026#34;true\u0026#34;); // -\u0026gt; \u0026#34;xx3.14true\u0026#34; #str::replace fastring replace(const char* s, size_t n, const char* sub, size_t m, const char* to, size_t l, size_t t=0); fastring replace(const char* s, size_t n, const char* sub, const char* to, size_t t=0); fastring replace(const char* s, const char* sub, const char* to, size_t t=0); fastring replace(const fastring\u0026amp; s, const char* sub, const char* to, size_t t=0); fastring replace(const std::string\u0026amp; s, const char* sub, const char* to, size_t t=0); Replace substring sub in string s with to, n is the length of s, m is the length of sub, and l is the length of to; t is the maximum number of replacements, the default is 0 for unlimited.\nThe content of the original string s remains unchanged, and the new string after replacement is returned.\nExample\nstr::replace(\u0026#34;xooxoox\u0026#34;, \u0026#34;oo\u0026#34;, \u0026#34;ee\u0026#34;); // -\u0026gt; \u0026#34;xeexeex\u0026#34; str::replace(\u0026#34;xooxoox\u0026#34;, \u0026#34;oo\u0026#34;, \u0026#34;ee\u0026#34;, 1); // -\u0026gt; \u0026#34;xeexoox\u0026#34; #str::split co::vector\u0026lt;fastring\u0026gt; split(const char* s, size_t n, char c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const char* s, size_t n, const char* c, size_t m, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const char* s, char c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const fastring\u0026amp; s, char c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const std::string\u0026amp; s, char c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const char* s, const char* c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const fastring\u0026amp; s, const char* c, size_t t=0); co::vector\u0026lt;fastring\u0026gt; split(const std::string\u0026amp; s, const char* c, size_t t=0); Split string s into several substrings by delimiter c. n is the length of s, m is the length of c, t is the maximum number of splits, and 0 means unlimited.\nThe content of the original string s remains unchanged.\nExample\nstr::split(\u0026#34;x y z\u0026#34;, \u0026#39;\u0026#39;); // -\u0026gt; [\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;, \u0026#34;z\u0026#34;] str::split(\u0026#34;|x|y|\u0026#34;,\u0026#39;|\u0026#39;); // -\u0026gt; [\u0026#34;\u0026#34;, \u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;] str::split(\u0026#34;xooy\u0026#34;, \u0026#34;oo\u0026#34;); // -\u0026gt; [\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;] str::split(\u0026#34;xooy\u0026#34;,\u0026#39;o\u0026#39;); // -\u0026gt; [\u0026#34;x\u0026#34;, \u0026#34;\u0026#34;, \u0026#34;y\u0026#34;] str::split(\u0026#34;xooy\u0026#34;,\u0026#39;o\u0026#39;, 1); // -\u0026gt; [\u0026#34;x\u0026#34;, \u0026#34;oy\u0026#34;] #str::strip template\u0026lt;typename ...X\u0026gt; inline fastring strip(X\u0026amp;\u0026amp; ...x) { return trim(std::forward\u0026lt;X\u0026gt;(x)...); } Equivalent to str::trim。 #str::trim fastring trim(const char* s, const char* c=\u0026#34; \\t\\r\\n\u0026#34;, char d=\u0026#39;b\u0026#39;); fastring trim(const char* s, char c, char d=\u0026#39;b\u0026#39;); fastring trim(const fastring\u0026amp; s, const char* c=\u0026#34; \\t\\r\\n\u0026#34;, char d=\u0026#39;b\u0026#39;); fastring trim(const fastring\u0026amp; s, char c, char d=\u0026#39;b\u0026#39;); Trim the string, removing character c or characters in string c from the left or right side of string s. The parameter d is the direction, \u0026rsquo;l\u0026rsquo; or \u0026lsquo;L\u0026rsquo; means left, \u0026lsquo;r\u0026rsquo; or \u0026lsquo;R\u0026rsquo; means right, default is \u0026lsquo;b\u0026rsquo; for both sides.\nThe content of the original string s remains unchanged.\nExample\nstr::trim(\u0026#34; xx\\r\\n\u0026#34;); // -\u0026gt; \u0026#34;xx\u0026#34; str::trim(\u0026#34;abxxa\u0026#34;, \u0026#34;ab\u0026#34;); // -\u0026gt; \u0026#34;xx\u0026#34; str::trim(\u0026#34;abxxa\u0026#34;, \u0026#34;ab\u0026#34;,\u0026#39;l\u0026#39;); // -\u0026gt; \u0026#34;xxa\u0026#34; str::trim(\u0026#34;abxxa\u0026#34;, \u0026#34;ab\u0026#34;,\u0026#39;r\u0026#39;); // -\u0026gt; \u0026#34;abxx\u0026#34; #Conversion #str::to_bool bool to_bool(const char* s); bool to_bool(const fastring\u0026amp; s); bool to_bool(const std::string\u0026amp; s); This function converts a string to bool type.\nWhen s is equal to \u0026ldquo;0\u0026rdquo; or \u0026ldquo;false\u0026rdquo;, false is returned; when s is equal to \u0026ldquo;1\u0026rdquo; or \u0026ldquo;true\u0026rdquo;, true is returned.\nIf the conversion is successful, the error code is 0. Otherwise, the error code is set to EINVAL, and false is returned. Call co::error() to get the error code.\nExample\nbool b = str::to_bool(\u0026#34;true\u0026#34;); // b = true bool x = str::to_bool(\u0026#34;false\u0026#34;); // x = false #str::to_double double to_double(const char* s); double to_double(const fastring\u0026amp; s); double to_double(const std::string\u0026amp; s); This function converts a string to double type.\nIf the conversion is successful, the error code is 0. Otherwise, the error code is set to ERANGE or EINVAL, and 0 is returned. Call co::error() to get the error code.\nExample\ndouble x = str::to_double(\u0026#34;3.14\u0026#34;); // x = 3.14 #str::to_int int32 to_int32(const char* s); int32 to_int32(const fastring\u0026amp; s); int32 to_int32(const std::string\u0026amp; s); int64 to_int64(const char* s); int64 to_int64(const fastring\u0026amp; s); int64 to_int64(const std::string\u0026amp; s); uint32 to_uint32(const char* s); uint32 to_uint32(const fastring\u0026amp; s); uint32 to_uint32(const std::string\u0026amp; s); uint64 to_uint64(const char* s); uint64 to_uint64(const fastring\u0026amp; s); uint64 to_uint64(const std::string\u0026amp; s); These functions convert a string to integer types.\nThe parameter s can take one unit k, m, g, t, p at the end, which is not case sensitive.\nIf the conversion is successful, the error code is 0. Otherwise, the error code is set to ERANGE or EINVAL, and 0 is returned. Call co::error() to get the error code.\nExample\nint32 i32; int64 i64; uint32 u32; uint64 u64; i32 = str::to_int32(\u0026#34;-23\u0026#34;); // -23 u32 = str::to_uint32(\u0026#34;4k\u0026#34;); // 4096 i64 = str::to_int32(\u0026#34;8M\u0026#34;); // 8 \u0026lt;\u0026lt; 20 i64 = str::to_int64(\u0026#34;8T\u0026#34;); // 8ULL \u0026lt;\u0026lt; 40 u64 = str::to_int64(\u0026#34;1P\u0026#34;); // 1ULL \u0026lt;\u0026lt; 50 i32 = str::to_int32(\u0026#34;8g\u0026#34;); LOG \u0026lt;\u0026lt; (i32 == 0); LOG \u0026lt;\u0026lt; (errno == ERANGE); i32 = str::to_int32(\u0026#34;abx\u0026#34;); LOG \u0026lt;\u0026lt; (i32 == 0); LOG \u0026lt;\u0026lt; (errno == EINVAL); #str::from template\u0026lt;typename T\u0026gt; inline fastring from(T t); This function converts built-in types to a string.\nT can be any built-in type, such as bool, int, double, void*, etc.\nExample\nfastring s; s = str::from(true); // -\u0026gt; \u0026#34;true\u0026#34; s = str::from(23); // -\u0026gt; \u0026#34;23\u0026#34; s = str::from(3.14); // -\u0026gt; \u0026#34;3.14\u0026#34; #str::dbg template\u0026lt;typename T\u0026gt; fastring dbg(const co::vector\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const std::vector\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const co::list\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const std::list\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const co::deque\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const std::deque\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const co::set\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const std::set\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const co::hash_set\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename T\u0026gt; fastring dbg(const std::unordered_set\u0026lt;T\u0026gt;\u0026amp; v); template\u0026lt;typename K, typename V\u0026gt; fastring dbg(const co::map\u0026lt;K, V\u0026gt;\u0026amp; v); template\u0026lt;typename K, typename V\u0026gt; fastring dbg(const std::map\u0026lt;K, V\u0026gt;\u0026amp; v); template\u0026lt;typename K, typename V\u0026gt; fastring dbg(const co::hash_map\u0026lt;K, V\u0026gt;\u0026amp; v); template\u0026lt;typename K, typename V\u0026gt; fastring dbg(const std::unordered_map\u0026lt;K, V\u0026gt;\u0026amp; v); template\u0026lt;typename K, typename V\u0026gt; fastring dbg(const co::lru_map\u0026lt;K, V\u0026gt;\u0026amp; v); This function converts a container to a debug string, which is generally for printing logs.\nExample\nstd::vector\u0026lt;int\u0026gt; v {1, 2, 3 }; std::set\u0026lt;int\u0026gt; s {1, 2, 3 }; std::map\u0026lt;int, int\u0026gt; m {{1, 1}, {2, 2} }; str::dbg(v); // -\u0026gt; \u0026#34;[1,2,3]\u0026#34; str::dbg(s); // -\u0026gt; \u0026#34;{1,2,3}\u0026#34; str::dbg(m); // -\u0026gt; \u0026#34;{1:1,2:2} std::vector\u0026lt;std::vector\u0026lt;int\u0026gt;\u0026gt; vv = { {1, 2, 3}, {6, 7, 8}, }; str::dbg(vv); // -\u0026gt; \u0026#34;[[1,2,3],[6,7,8]]\u0026#34; Since v3.0.1, str::dbg supports multiple nested containers. "},{"id":29,"href":"/en/co/unitest/","title":"Unitest","section":"Documents","content":"include: co/unitest.h.\n#Basic concepts co.unitest is a unit testing framework, similar to google gtest, but easier to use.\n#Test Units and Test Cases A test program can be divided into multiple test units according to functions or modules, and there can be multiple test cases under each test unit. For example, a test unit can be defined for a class (or module) in C++, and a test case can be defined for each method in the class (or module).\nDEF_test(test_name) { DEF_case(a) { // write test code here } DEF_case(b) { // write test code here } } In the above example, DEF_test defines a test unit (actually defines a function), and DEF_case defines the test case, a test case is actually a code block in the function.\n#DEF_test #define DEF_test(_name_) \\ DEF_bool(_name_, false, \u0026#34;enable this test if true\u0026#34;); \\ ... \\ void _co_ut_##_name_(unitest::xx::Test\u0026amp; _t_) The DEF_test macro is used to define a test unit, and the parameter _name_ is the name of the test unit. The first line of the macro defines a bool flag, which is the switch of the test unit. For example, DEF_test(os) defines a test unit os, and we can use -os in command line to enable test cases in this unit. The last line of the macro defines the function corresponding to the test unit. #DEF_case #define DEF_case(name) \\ _t_.c = #name; \\ cout \u0026lt;\u0026lt; \u0026#34; case \u0026#34; \u0026lt;\u0026lt; #name \u0026lt;\u0026lt; \u0026#39;:\u0026#39; \u0026lt;\u0026lt; endl; The DEF_case macro is used to define a test case in the test unit. The parameter name is the name of the test case. It must be used inside the function defined by DEF_test. The name of a test unit must be albe to use as part of the class name or variable name. The test case name does not have this restriction. For example, DEF_case(sched.Copool) is also reasonable. The code of the test case is generally enclosed by a pair of curly braces to isolate it from other test cases. DEF_test may not contain any DEF_case. In this case, co.unitest will create a default test case. #EXPECT assertion #define EXPECT(x) ... #define EXPECT_EQ(x, y) EXPECT_OP(x, y, ==, \u0026#34;EQ\u0026#34;) #define EXPECT_NE(x, y) EXPECT_OP(x, y, !=, \u0026#34;NE\u0026#34;) #define EXPECT_GE(x, y) EXPECT_OP(x, y, \u0026gt;=, \u0026#34;GE\u0026#34;) #define EXPECT_LE(x, y) EXPECT_OP(x, y, \u0026lt;=, \u0026#34;LE\u0026#34;) #define EXPECT_GT(x, y) EXPECT_OP(x, y, \u0026gt;, \u0026#34;GT\u0026#34;) #define EXPECT_LT(x, y) EXPECT_OP(x, y, \u0026lt;, \u0026#34;LT\u0026#34;) EXPECT asserts that x is true, and x can be any expression with a value of type bool. EXPECT_EQ asserts x == y. EXPECT_NE asserts x != y. EXPECT_GE asserts that x \u0026gt;= y. EXPECT_LE asserts that x \u0026lt;= y. EXPECT_GT asserts that x \u0026gt; y. EXPECT_LT asserts that x \u0026lt; y. When defining a test case with DEF_case, you can use these macro assertions. If an assertion fails, it means that the test case fails. The terminal will print related error messages in red color. #Write test code #Test code example #include \u0026#34;co/unitest.h\u0026#34; #include \u0026#34;co/os.h\u0026#34; DEF_test(os) { DEF_case(homedir) { EXPECT_NE(os::homedir(), \u0026#34;\u0026#34;); } DEF_case(pid) { EXPECT_GE(os::pid(), 0); } DEF_case(cpunum) { EXPECT_GT(os::cpunum(), 0); } } int main(int argc, char** argv) { flag::parse(argc, argv); unitest::run_tests(); return 0; } The above code defines a test unit named os, and os has 3 test cases. When running the test program, you can use -os in the command line to enable this unit test. In the main function, you need call flag::parse() to parse the command line parameters, and then call the run_tests() method provided by co.unitest to run the unit test code. #Default test case DEF_test(os) { EXPECT_NE(os::homedir(), \u0026#34;\u0026#34;); EXPECT_GE(os::pid(), 0); EXPECT_GT(os::cpunum(), 0); } The above code does not contain any DEF_case, co.unitest will create a default test case named default. For more complex test codes, it is generally not recommended to use the default test cases. It is better to divide them into different cases so that the code looks clearer. #Build and run the test program #Build the unitest code xmake -b unitest Run the above command in the root directory of coost to compile the unit test code in co/unitest and generate the unitest binary program . #Run all test cases xmake r unitest Run all test cases by default. #Run test cases in specified test units # Run only test cases in the os test unit xmake r unitest -os # Run test cases in the os or json test units xmake r unitest -os -json #Test result example All tests passed Test case failed "},{"id":30,"href":"/en/co/benchmark/","title":"Benchmark","section":"Documents","content":"include: co/benchmark.h.\n#basic concept co.benchmark is a benchmark framework added in v3.0.1, which can be used for performance benchmarking.\n#BM_group #define BM_group(_name_) \\ ......\\ void _co_bm_group_##_name_(bm::xx::Group\u0026amp; _g_) The BM_group macro is used to define a benchmark group, which actually defines a function. Multiple benchmarks can be defined with BM_add in each group. The parameter _name_ is the group name, which is also part of name of the defined function. BM_group(atomic) is ok, but BM_group(co.atomic) is not allowed, as co.atomic can\u0026rsquo;t be used in function name. #BM_add #define BM_add(_name_) \\ _g_.bm = #_name_; \\ _BM_add The BM_add macro is used to define a benchmark, it must be used inside the function defined by BM_group. The parameter _name_ is the benchmark name, unlike BM_group, BM_add(co.atomic) is also allowed. #BM_use #define BM_use(v) bm::xx::use(\u0026amp;v, sizeof(v)) The BM_use macro tells the compiler that the variable v will be used, which prevents the compiler from optimizing away some test code. #Write benchmark code #Code example #include \u0026#34;co/benchmark.h\u0026#34; #include \u0026#34;co/mem.h\u0026#34; BM_group(malloc) { void* p; BM_add(::malloc)( p = ::malloc(32); ); BM_use(p); BM_add(co::alloc)( p = co::alloc(32); ); BM_use(p); } int main(int argc, char** argv) { flag::parse(argc, argv); bm::run_benchmarks(); return 0; } The code above defines a benchmark group called malloc, and 2 benchmarks are added to it with BM_add. Calling bm::run_benchmarks() will execute all the benchmark code. In the above example, if there is no BM_use(p), the compiler may think that p is an unused variable, and optimize away the relevant test code, resulting in the inability to measure accurate results. #Result example The result of the benchmark test is printed as a markdown table, and it can be easily copied to a markdown document. Multiple BM_group will generate multiple markdown tables. The first column of the table is all benchmarks in the group, the second column is the time of a single iteration (in nanoseconds), the third column is the number of iterations per second, and the fourth column is the performance improvement multiple, based on the first benchmark. "},{"id":31,"href":"/en/co/other/rand/","title":"Random Value","section":"others","content":"include: co/rand.h.\n#Random number (co::rand) 1. uint32 rand(); 2. uint32 rand(uint32\u0026amp; seed); 1, returns a random number between 1 and 2G-2, thread-safe. 2, returns a random number between 1 and 2G-2 with the specified seed number, which must be between 1 and 2G-2. This function will update the value of seed, it is not thread-safe. The value of seed in 2 can be initialized with the return value in 1. example uint32 x = co::rand(); uint32 y = co::rand(); uint32 seed = co::rand(); uint32 u = co::rand(seed); uint32 v = co::rand(seed); #Random string (co::randstr) 1. fasting randstr(int n=15); 2. fasting randstr(const char* s, int n); 1, returns a random string of length n (15 by default), thread-safe. 2, returns a random string of length n composed of characters in string s. Abbreviations like 0-9, a-z can be used in s. The expanded length of s cannot exceed 255. Thread-safe. randstr is implemented based on the algorithm of nanoid. When the returned random string is long enough, it can be used as a unique id. example fasting s = co::randstr(); s = co::randstr(8); s = co::randstr(\u0026#34;0123456789\u0026#34;, 6); s = co::randstr(\u0026#34;0-9a-f\u0026#34;, 8); // 8-byte hex string "},{"id":32,"href":"/en/co/net/rpc/","title":"RPC","section":"Network Programming","content":"include: co/rpc.h.\nCoost implements a coroutine-based RPC framework, which internally uses JSON as the data exchange format. Compared with RPC frameworks using binary protocols such as protobuf, it is more flexible and easier to use.\nSince v3.0, the RPC framework also supports HTTP protocol, and we are able to send a RPC request with the HTTP POST method. #rpc::Service class Service { public: Service() = default; virtual ~Service() = default; typedef std::function\u0026lt;void(Json\u0026amp;, Json\u0026amp;)\u0026gt; Fun; virtual const char* name() const = 0; virtual const co::map\u0026lt;const char*, Fun\u0026gt;\u0026amp; methods() const = 0; }; This class is a pure interface, which represents a RPC service. A RPC server may have multiple services. name() returns the service name, methods() returns all the RPC methods. #rpc::Server #Server::Server Server(); The default constructor, users do not need to care. #Server::add_service 1. Server\u0026amp; add_service(rpc::Service* s); 2. Server\u0026amp; add_service(const std::shared_ptr\u0026lt;rpc::Service\u0026gt;\u0026amp; s); Add a service, the parameter s in the first one must be dynamically created with operator new. Users can call this method multiple times to add multiple services, and different services must have different names. #Server::start void start( const char* ip, int port, const char* url=\u0026#34;/\u0026#34;, const char* key=0, const char* ca=0 ); Start the RPC server, this method will not block the current thread. The parameter ip is the server ip, which can be an IPv4 or IPv6 address, and the parameter port is the server port. The parameter url is the url of the HTTP server, and must start with /. The parameter key is path of a PEMfile which stores the SSL private key, and the parameter ca is path of a PEM file which stores the SSL certificate. They are NULL by default, and SSL is disabled. Starting from v3.0, the server no longer depends on the rpc::Server object after startup. #Server::exit void exit(); Added since v2.0.2. Exit the RPC server, close the listening socket, and no longer receive new connections. Since v3.0, after the RPC server exits, previously established connections will be reset in the future. #RPC server example #Define a proto file Here is a simple proto file hello_world.proto:\npackage xx service HelloWorld { hello world } package defines the package name.\npackage defines the package name, which corresponds to namespace in C++.\nservice defines a RPC service, it has 2 methods, hello and world.\nSince the RPC request and response are both JSON, there is no need to define the structure in the proto file.\nAt most one service can be defined in a proto file.\nCoost v3.0.1 rewrote gen with flex and byacc, and we can alse define object (struct) in the proto file. For specific usage, please refer to j2s. #Generate code for RPC service gen is the RPC code generator provided by coost, which can be used to generate code for RPC service.\nxmake -b gen # build gen cp gen /usr/local/bin # put gen in the /usr/local/bin directory gen hello_world.proto # Generate code The generated file hello_world.proto is as follow:\n// Autogenerated. // DO NOT EDIT. All changes will be undone. #pragma once #include \u0026#34;co/rpc.h\u0026#34; namespace xx { class HelloWorld : public rpc::Service { public: typedef std::function\u0026lt;void(Json\u0026amp;, Json\u0026amp;)\u0026gt; Fun; HelloWorld() { using std::placeholders::_1; using std::placeholders::_2; _methods[\u0026#34;HelloWorld.hello\u0026#34;] = std::bind(\u0026amp;HelloWorld::hello, this, _1, _2); _methods[\u0026#34;HelloWorld.world\u0026#34;] = std::bind(\u0026amp;HelloWorld::world, this, _1, _2); } virtual ~HelloWorld() {} virtual const char* name() const { return \u0026#34;HelloWorld\u0026#34;; } virtual const co::map\u0026lt;const char*, Fun\u0026gt;\u0026amp; methods() const { return _methods; } virtual void hello(Json\u0026amp; req, Json\u0026amp; res) = 0; virtual void world(Json\u0026amp; req, Json\u0026amp; res) = 0; private: co::map\u0026lt;const char*, Fun\u0026gt; _methods; }; } // xx As you can see, the HelloWorld class inherits from rpc::Service, and it has already implemented name() and methods() in rpc::Service. Users only need to inherit the HelloWorld class and implement the methods hello and world. #Implement the RPC service #include \u0026#34;hello_world.h\u0026#34; namespace xx { class HelloWorldImpl : public HelloWorld { public: HelloWorldImpl() = default; virtual ~HelloWorldImpl() = default; virtual void hello(Json\u0026amp; req, Json\u0026amp; res) { res = { { \u0026#34;result\u0026#34;, { { \u0026#34;hello\u0026#34;, 23 } }} }; } virtual void world(Json\u0026amp; req, Json\u0026amp; res) { res = { { \u0026#34;error\u0026#34;, \u0026#34;not supported\u0026#34;} }; } }; } // xx The above is just a very simple example. In actual applications, it is generally necessary to perform corresponding business processing according to the parameters in req, and then fill in res. #Start RPC server int main(int argc, char** argv) { flag::parse(argc, argv); rpc::Server() .add_service(new xx::HelloWorldImpl) .start(\u0026#34;127.0.0.1\u0026#34;, 7788, \u0026#34;/xx\u0026#34;); for (;;) sleep::sec(80000); return 0; } } First call add_service() to add the service, then call start() to start the server. The start() method will not block the current thread, so we need a for loop to prevent the main function from exiting. #Call RPC service with curl In v3.0, the RPC framework supports HTTP protocol, so we can call the RPC service with the curl command:\ncurl http://127.0.0.1:7788/xx --request POST --data \u0026#39;{\u0026#34;api\u0026#34;:\u0026#34;ping\u0026#34;}\u0026#39; curl http://127.0.0.1:7788/xx --request POST --data \u0026#39;{\u0026#34;api\u0026#34;:\u0026#34;HelloWorld.hello\u0026#34;}\u0026#39; The above use curl to send a POST request to the RPC server, the parameter is a JSON string, and a \u0026quot;api\u0026quot; field must be provided to indicate the RPC method to be called.\n\u0026quot;ping\u0026quot; is a built-in method of the RPC framework, generally used for testing or sending heartbeats.\n/xx in the url should be consistent with the url specified when the RPC server is started.\n#rpc::Client #Client::Client 1. Client(const char* ip, int port, bool use_ssl=false); 2. Client(const Client\u0026amp; c); 1, the parameter ip is ip of the server, which can be a domain name, IPv4 or IPv6 address; the parameter port is port of the server; the parameter use_ssl indicates whether to enable SSL transmission, the default is false, and SSL is disabled. When rpc::Client was constructed, the connection is not established immediately. #Client::~Client Client::~Client(); Destructor, close the internal connection. #Client::call void call(const Json\u0026amp; req, Json\u0026amp; res); Perform a RPC request, it must be called in coroutine. The parameter req must contain the \u0026quot;api\u0026quot; field, its value is generally in the form of \u0026quot;service.method\u0026quot;. The parameter res is the response of the RPC request. If the RPC request is not sent, or no response from the server is received, res will not be filled. This method checks the connection status before sending the RPC request, and establishes the connection first if it is not connected. #Client::close void close(); Close the connection, it is safe to call this function multiple times. #Client::ping void ping(); Send a ping request to the server, generally used for testing or sending heartbeats. #RPC client example #Use rpc::Client directly DEF_bool(use_ssl, false, \u0026#34;use ssl if true\u0026#34;); DEF_int32(n, 3, \u0026#34;request num\u0026#34;); void client_fun() { rpc::Client c(\u0026#34;127.0.0.1\u0026#34;, 7788, FLG_use_ssl); for (int i = 0; i \u0026lt; FLG_n; ++i) { co::Json req = { {\u0026#34;api\u0026#34;, \u0026#34;HelloWorld.hello\u0026#34;} }; co::Json res; c.call(req, res); co::sleep(1000); } c.close(); } go(client_fun); In the above example, the client sends an RPC request to the server every 1 second. #Use connection pool When a client needs to establish a large number of connections, co::pool can be used to manage these connections.\nstd::unique_ptr\u0026lt;rpc::Client\u0026gt; proto; co::pool pool( []() { return (void*) new rpc::Client(*proto); }, [](void* p) { delete (rpc::Client*) p; } ); void client_fun() { co::pool_guard\u0026lt;rpc::Client\u0026gt; c(pool); while (true) { c-\u0026gt;ping(); co::sleep(3000); } } proto.reset(new rpc::Client(\u0026#34;127.0.0.1\u0026#34;, 7788)); for (int i = 0; i \u0026lt; 8; ++i) { go(client_fun); } In the above example, co::pool is used to store the clients, and multiple coroutines can share these clients. The ccb of co::pool uses copy construction to copy a client from proto. #Config items Coost uses co.flag to define config items for RPC.\n#rpc_conn_idle_sec DEF_int32(rpc_conn_idle_sec, 180, \u0026#34;#2 connection may be closed if no data...\u0026#34;); Timeout in seconds for idle connections in rpc::Server. If a connection does not receive any data within this time, the server may close the connection. #rpc_conn_timeout DEF_int32(rpc_conn_timeout, 3000, \u0026#34;#2 connect timeout in ms\u0026#34;); Connect timeout in milliseconds for rpc::Client. #rpc_log DEF_bool(rpc_log, true, \u0026#34;#2 enable rpc log if true\u0026#34;); Whether to print RPC logs, the default is true, rpc::Server and rpc::Client will print RPC requests and responses. #rpc_max_idle_conn DEF_int32(rpc_max_idle_conn, 128, \u0026#34;#2 max idle connections\u0026#34;); Maximum number of idle connections for rpc::Server. The default is 128. When this number is exceeded, the server will close some idle connections. #rpc_max_msg_size DEF_int32(rpc_max_msg_size, 8 \u0026lt;\u0026lt; 20, \u0026#34;#2 max size of rpc message, default: 8M\u0026#34;); The maximum length of RPC messages, the default is 8M. #rpc_recv_timeout DEF_int32(rpc_recv_timeout, 3000, \u0026#34;#2 recv timeout in ms\u0026#34;); RPC recv timeout in milliseconds. #rpc_send_timeout DEF_int32(rpc_send_timeout, 3000, \u0026#34;#2 send timeout in ms\u0026#34;); RPC send timeout in milliseconds. "},{"id":33,"href":"/en/co/concurrency/coroutine/wg/","title":"Wait Group","section":"Coroutine","content":"include: co/co.h.\n#co::wait_group co::wait_group is similar to sync.WaitGroup in golang and can be used to wait for the exit of a coroutine or thread.\n#constructor 1. explicit wait_group(uint32 n); 2. wait_group(); 3. wait_group(wait_group\u0026amp;\u0026amp; wg); 4. wait_group(const wait_group\u0026amp; wg); 1, initializes the internal counter to n. 2, default constructor, initializes the internal counter to 0. 3, move constructor. 4, copy constructor, only increases the internal reference count by 1. #add void add(uint32 n=1) const; Increment internal counter by n, n defaults to 1. #done void done() const; Decrement internal counter by 1. #wait void wait() const; Wait until the internal counter reaches 0. #Code Example #include \u0026#34;co/co.h\u0026#34; #include \u0026#34;co/cout.h\u0026#34; DEF_uint32(n, 8, \u0026#34;coroutine number\u0026#34;); int main(int argc, char** argv) { flag::parse(argc, argv); co::wait_group wg; wg.add(FLG_n); for (uint32 i = 0; i \u0026lt; FLG_n; ++i) { go([wg]() { co::print(\u0026#34;sched: \u0026#34;, co::sched_id(), \u0026#34; co: \u0026#34;, co::coroutine_id()); wg.done(); }); } wg. wait(); return 0; } "},{"id":34,"href":"/en/co/concurrency/coroutine/pool/","title":"Coroutine Pool","section":"Coroutine","content":"include: co/co.h.\n#co::pool co::pool is a general-purpose coroutine pool, which is coroutine-safe and internally stores void* type pointers, which can be used as connection pool, memory pool or caches for other purposes.\n#constructor 1. pool(); 2. pool(pool\u0026amp;\u0026amp; p); 3. pool(const pool\u0026amp; p); 4. pool(std::function\u0026lt;void*()\u0026gt;\u0026amp;\u0026amp; ccb, std::function\u0026lt;void(void*)\u0026gt;\u0026amp;\u0026amp; dcb, size_t cap=(size_t)-1); 1, default constructor, compared to 4, ccb and dcb are NULL.\n2, move constructor.\n3, copy constructor, only increases the internal reference count by 1.\n4, ccb is used to create an element, and dcb is used to destroy an element, cap specifies the maximum capacity of the pool, the default is -1, unlimited capacity.\nNote that the parameter cap is not the total capacity. It is for a single thread. In the internal implementation of co::pool, each thread has its own pool. If the cap is set to 1024 and there are 8 scheduling threads, then the total capacity is 8192.\nWhen dcb is NULL, parameter cap will be ignored, because co::pool needs to use dcb to destroy extra elements when the number of elements exceeds the maximum capacity.\nExample\nclass T {}; co::pool p( []() { return (void*) new T; }, // ccb [](void* p) { delete (T*) p; }, // dcb ); #clear void clear() const; Clear the pool, can be called anywhere. If dcb is set, elements in the pool will be destroyed with dcb. #pop void* pop() const; Pop an element from the pool, Must be called in coroutine. When the pool is empty and ccb is not NULL, ccb() will be called to create an element and return, otherwise return NULL. This method is coroutine safe and does not require locking. #push void push(void* e) const; Put the element back to the pool, must be called in coroutine. When parameter e is NULL, it is ignored. Since each thread has its own pool internally, push() and pop() methods need to be called in the same thread. If the pool has reached the maximum capacity and dcb is not NULL, dcb(e) will be called to destroy the element. This method is coroutine safe and does not require locking. #size size_t size() const; Returns the pool size of the current thread, must be called in the coroutine. #co::pool_guard co::pool_guard automatically pops an element from co::pool when it is constructed, and automatically puts the element back when it is destructed.\ntemplate\u0026lt;typename T\u0026gt; class pool_guard; Parameter T is the actual type pointed to by the pointer in co::pool. #constructor explicit pool_guard(co::pool\u0026amp; p); explicit pool_guard(co::pool* p); Pop an element from co::pool, the parameter p is a reference or pointer to the co::pool class. #destructor ~pool_guard(); Push the element obtained in the constructor back to co::pool. #get T* get() const; Get the pointer taken from co::pool. #operator-\u0026gt; T* operator-\u0026gt;() const; Returns the pointer taken from co::pool. #operator* T\u0026amp; operator*() const; Returns a reference to the object pointed to by the internal pointer. #operator== bool operator==(T* p) const; Determine whether the internal pointer is equal to p. #operator!= bool operator!=(T* p) const; Determine whether the internal pointer is not equal to p. #operator bool explicit operator bool() const; Returns true if the internal pointer is not NULL, false otherwise. #Code Example class Redis; // assume class Redis is a connection to the redis server co::pool p( []() { return (void*) new Redis; }, // ccb [](void* p) { delete (Redis*) p; }, // dcb 8192 //cap ); void f() { co::pool_guard\u0026lt;Redis\u0026gt; rds(p); rds-\u0026gt;get(\u0026#34;xx\u0026#34;); } go(f); "},{"id":35,"href":"/en/co/other/hash/","title":"Hash","section":"others","content":"include: co/hash.h.\n#Hash #hash32 uint32 hash32(const void* s, size_t n); uint32 hash32(const char* s); uint32 hash32(const fastring\u0026amp; s) uint32 hash32(const std::string\u0026amp; s); This function returns a 32-bit murmur hash value. When s is a pointer, it is required to be sizeof(void*) byte aligned. #hash64 uint64 hash64(const void* s, size_t n); uint64 hash64(const char* s); uint64 hash64(const fastring\u0026amp; s); uint64 hash64(const std::string\u0026amp; s); This function returns a 64-bit murmur hash value. When s is a pointer, it is required to be 8-byte aligned. #murmur_hash size_t murmur_hash(const void* s, size_t n); This function returns a hash value of size_t type. This value is 64-bit on 64-bit platform and 32-bit on 32-bit platform. The parameter s is generally required to be sizeof(void*) byte-aligned. #md5 #md5digest void md5digest(const void* s, size_t n, char res[16]); fastring md5digest(const void* s, size_t n); fastring md5digest(const char* s); fastring md5digest(const fastring\u0026amp; s); fastring md5digest(const std::string\u0026amp; s); This function calculates the md5 of a string and returns a 16-byte binary string. #md5sum void md5sum(const void* s, size_t n, char res[32]); fastring md5sum(const void* s, size_t n); fastring md5sum(const char* s); fastring md5sum(const fastring\u0026amp; s); fastring md5sum(const std::string\u0026amp; s); This function calculates the md5 of a string and returns a 32-byte string containing only hexadecimal characters(0-9,a-f). #Lower level APIs void md5_init(md5_ctx_t* ctx); void md5_update(md5_ctx_t* ctx, const void* s, size_t n); void md5_final(md5_ctx_t* ctx, uint8 res[16]); The above 3 APIs can be used to calculate md5 incrementally.\nExample\nchar buf[4096]; uint8 res[16]; md5_ctx_t ctx; md5_init(\u0026amp;ctx); while (true) { int r = read(fd, buf, 4096); if (r \u0026gt; 0) { md5_update(\u0026amp;ctx, buf, r); } else { break; } } md5_final(\u0026amp;ctx, res); #sha256 #sha256digest void sha256digest(const void* s, size_t n, char res[32]); fastring sha256digest(const void* s, size_t n); fastring sha256digest(const char* s); fastring sha256digest(const fastring\u0026amp; s); fastring sha256digest(const std::string\u0026amp; s); This function calculates the sha256 of a string and returns a 32-byte binary string. #sha256sum void sha256sum(const void* s, size_t n, char res[64]); fastring sha256sum(const void* s, size_t n); fastring sha256sum(const char* s); fastring sha256sum(const fastring\u0026amp; s); fastring sha256sum(const std::string\u0026amp; s); This function calculates the sha256 of a string and returns a 64-byte string containing only hexadecimal characters(0-9,a-f). #Lower level APIs void sha256_init(sha256_ctx_t* ctx); void sha256_update(sha256_ctx_t* ctx, const void* s, size_t n); void sha256_final(sha256_ctx_t* ctx, uint8 res[32]); The above 3 APIs can be used to calculate sha256 incrementally like that in md5. #base64 #base64_encode fastring base64_encode(const void* s, size_t n); fastring base64_encode(const char* s); fastring base64_encode(const fastring\u0026amp; s); fastring base64_encode(const std::string\u0026amp; s); base64 encoding, \\r\\n is not added in this implementation. #base64_decode fastring base64_decode(const void* s, size_t n); fastring base64_decode(const char* s); fastring base64_decode(const fastring\u0026amp; s); fastring base64_decode(const std::string\u0026amp; s); base64 decoding, if the input is not valid base64-encoded data, the decoding will fail and an empty string will be returned. #url #url_encode fastring url_encode(const void* s, size_t n); fastring url_encode(const char* s); fastring url_encode(const fastring\u0026amp; s); fastring url_encode(const std::string\u0026amp; s); url encoding, reserved characters !()*#$\u0026amp;'+,/:;=?@[] and a-z A-Z 0-9 -_.~ will not be encoded, all other characters will be encoded. #url_decode fastring url_decode(const void* s, size_t n); fastring url_decode(const char* s); fastring url_decode(const fastring\u0026amp; s); fastring url_decode(const std::string\u0026amp; s); url decoding, if the input is not a reasonably encoded url, the decoding will fail and an empty string will be returned. #crc16 uint16_t crc16(const void* s, size_t n); uint16_t crc16(const char* s); uint16_t crc16(const fastring\u0026amp; s); uint16_t crc16(const std::string\u0026amp; s); This function calculates the crc16 value of a string. The implementation is taken from redis and will be used when implementing the redis cluster client. "},{"id":36,"href":"/en/co/json/","title":"JSON","section":"Documents","content":"include: co/json.h.\nco.json is a JSON library similar to rapidjson. Compared with rapidjson, it has better performance and is easier to use.\n#Basic concepts JSON is a simple data format that supports two data structures:\nA collection consisting of a series of key/value pairs. This type of structure is called object, which corresponds to struct, map, etc, in programming languages. A list composed of a series of value, this kind of structure is called array, which corresponds to vector, list, etc, in programming languages. In the above, the key is a string, and the value is generally called JSON value, which can be any of object, array, number, string, bool(false, true), or null. number is an integer or a floating-point number, and most implementations will distinguish integers from floating-point numbers.\nObject is enclosed by a pair of braces, array is enclosed by a pair of square brackets, they look like this:\n{\u0026#34;a\u0026#34;:1, \u0026#34;b\u0026#34;:false, \u0026#34;s\u0026#34;:\u0026#34;xxx\u0026#34;} [1, 2, 3] By definition, object and array can be nested, which can represent complex data structures such as trees.\n#global #json::array Json array(); This function is in namespace json, it returns an empty array. #json::object Json object(); This function is in namespace json, it returns an empty object. #json::parse Json parse(const char* s, size_t n); Json parse(const char* s); Json parse(const fastring\u0026amp; s); Json parse(const std::string\u0026amp; s); Parse Json from a JSON string. This function is not a method in the Json class, but a function defined in namespace json. This function returns a Json object, when the parsing failed, it returns null. #co::Json #constructor 1. Json() noexcept; 2. Json(decltype(nullptr)) noexcept; 3. Json(Json\u0026amp;\u0026amp; v) noexcept; 4. Json(Json\u0026amp; v) noexcept; Json(const Json\u0026amp; v) = delete; 5. Json(bool v); 6. Json(double v); 7. Json(int64 v); 8. Json(int32 v); 9. Json(uint32 v); 10. Json(uint64 v); 11. Json(const void* p, size_t n); 12. Json(const char* s); 13. Json(const fastring\u0026amp; s); 14. Json(const std::string\u0026amp; s); 15. Json(std::initializer_list\u0026lt;Json\u0026gt; v); 1-2, construct a null object.\n3-4, move constructor and copy constructor, both implement a move semantic, the parameter v will become a null object after the construction.\n5, construct a JSON object of bool type.\n6, construct a JSON object of double type.\n7-10, construct a JSON object of integer type.\n11-14, construct a JSON object of string type.\n15, construct a JSON object of object or array type from a initialization list.\nExample\nco::Json a; // null co::Json b(nullptr); // null co::Json c = false; // bool co::Json d = 3.14; // double co::Json e = 23; // integer co::Json f = \u0026#34;xx\u0026#34;; // string co::Json g = {1, 2, 3}; // g -\u0026gt; [1, 2, 3] co::Json h = {\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;}; // h -\u0026gt; [\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;] co::Json i = { // i -\u0026gt; { \u0026#34;a\u0026#34;: \u0026#34;b\u0026#34; } {\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;} }; co::Json j = { // j -\u0026gt; {\u0026#34;a\u0026#34;: 1, \u0026#34;b\u0026#34;: [1,2,3]} {\u0026#34;a\u0026#34;, 1}, {\u0026#34;b\u0026#34;, {1, 2, 3}}, }; co::Json x(i); // i -\u0026gt; null co::Json y(std::move(j)); // j -\u0026gt; null #operator= Json\u0026amp; operator=(Json\u0026amp;\u0026amp; v); Json\u0026amp; operator=(Json\u0026amp; v); void operator=(const Json\u0026amp;) = delete; Assignment, the 2 methods above are equal, v is moved to the calling Json object, and becomes a null object after the operation. #dup Json dup() const; Return a deep copy of a JSON object.\n示例\nco::Json x = {1, 2, 3}; // x -\u0026gt; [1,2,3] co::Json y, z; y = x; // x -\u0026gt; null, y -\u0026gt; [1,2,3] z = y.dup(); // y:[1,2,3], z -\u0026gt; [1,2,3] #——————————— #is_null bool is_null() const; Determine whether the Json is null. #is_bool bool is_bool() const; Determine whether the Json is bool type. #is_int bool is_int() const; Determine whether the Json is integer type. #is_double bool is_double() const; Determine whether the Json is double type. #is_string bool is_string() const; Determine whether the Json is string type. #is_array bool is_array() const; Determine whether the Json is array type. #is_object bool is_object() const; Determine whether the Json is object type. #——————————— #as_bool bool as_bool() const; Get value of bool type. For int or double types, returns false if the value is 0, otherwise returns true. For string type, returns true if the value is \u0026quot;true\u0026quot; or \u0026quot;1\u0026quot;, otherwise returns false. For other non-bool types, return false. #as_int int as_int() const; int32 as_int32() const; int64 as_int64() const; Get value of integer type. For bool, double or string types, the result is automatically converted to an integer. For other non-integer types, 0 is returned. #as_double double as_double() const; Get value of double type. Return 0 if the Json object calling this method is not double type. Get value of double type. For bool, int or string types, the result is automatically converted to double type. For other non-double types, 0 is returned. #as_string fastring as_string() const; Get value of string type, return fastring. For non-string types, this method is equal to str(), and the result will be automatically converted to string type. #as_c_str const char* as_c_str() const; Returns a null-terminated C-style string, string_size() can be called to get its length. For non-string types, return an empty string. #get 1. Json\u0026amp; get(uint32 i) const; 2. Json\u0026amp; get(int i) const; 3. Json\u0026amp; get(const char* key) const; 4. template \u0026lt;class T, class ...X\u0026gt; inline Json\u0026amp; get(T\u0026amp;\u0026amp; v, X\u0026amp;\u0026amp; ... x) const; Get JSON object according to index or key. This method is a read-only operation and will not modify the JSON object that calls this method. 1-2, get the i-th element of the array object. If the JSON object that calls this method is not of type array, or i exceeds the range of the array, the returned result will refer to a null object. 3, get the JSON value corresponding to key, if the JSON object called this method is not of type object, or key does not exist, the returned result will refer to a null object. 4, can take any number of parameters, each parameter is an index or a key. When it encounters the first invalid index or non-existing key, it returns immediately, and the return result will refer to a null object. #set template \u0026lt;class T\u0026gt; inline Json\u0026amp; set(T\u0026amp;\u0026amp; v) { return *this = Json(std::forward\u0026lt;T\u0026gt;(v)); } template \u0026lt;class A, class B, class ...X\u0026gt; inline Json\u0026amp; set(A\u0026amp;\u0026amp; a, B\u0026amp;\u0026amp; b, X\u0026amp;\u0026amp; ... x); Set the value of the JSON object. The last parameter of set is the value to be set, other parameters are index or key. #Example co::Json r = { { \u0026#34;a\u0026#34;, 7 }, { \u0026#34;b\u0026#34;, false }, { \u0026#34;c\u0026#34;, { 1, 2, 3 } }, { \u0026#34;s\u0026#34;, \u0026#34;23\u0026#34; }, }; r.get(\u0026#34;a\u0026#34;).as_int(); // 7 r.get(\u0026#34;b\u0026#34;).as_bool(); // false r.get(\u0026#34;s\u0026#34;).as_string(); // \u0026#34;23\u0026#34; r.get(\u0026#34;s\u0026#34;).as_int(); // 23 r.get(\u0026#34;c\u0026#34;, 0).as_int(); // 1 r.get(\u0026#34;c\u0026#34;, 1).as_int(); // 2 // x -\u0026gt; {\u0026#34;a\u0026#34;:1,\u0026#34;b\u0026#34;:[0,1,2],\u0026#34;c\u0026#34;:{\u0026#34;d\u0026#34;:[\u0026#34;oo\u0026#34;]}} co::Json x; x.set(\u0026#34;a\u0026#34;, 1); x.set(\u0026#34;b\u0026#34;, co::Json({0,1,2})); x.set(\u0026#34;c\u0026#34;, \u0026#34;d\u0026#34;, 0, \u0026#34;oo\u0026#34;); #——————————— #operator== bool operator==(bool v) const; bool operator==(double v) const; bool operator==(int64 v) const; bool operator==(int v) const; bool operator==(uint32 v) const; bool operator==(uint64 v) const; bool operator==(const char* v) const; bool operator==(const fastring\u0026amp; v) const; bool operator==(const std::string\u0026amp; v) const; Check if the value of the Json object is equal to v. If the type of the Json object is different from v, return false directly. #operator!= bool operator!=(bool v) const; bool operator!=(double v) const; bool operator!=(int64 v) const; bool operator!=(int v) const; bool operator!=(uint32 v) const; bool operator!=(uint64 v) const; bool operator!=(const char* v) const; bool operator!=(const fastring\u0026amp; v) const; bool operator!=(const std::string\u0026amp; v) const; Check if the value of the Json object is not equal to v. If the type of the Json object is different from v, return true directly. #Example co::Json x = { {\u0026#34;a\u0026#34;, 3}, {\u0026#34;b\u0026#34;, false}, {\u0026#34;s\u0026#34;, \u0026#34;xx\u0026#34;}, }; x == 7; // false x[\u0026#34;a\u0026#34;] == 3; // true x[\u0026#34;b\u0026#34;] == false; // true x[\u0026#34;s\u0026#34;] == \u0026#34;xx\u0026#34;; // true #——————————— #add_member Json\u0026amp; add_member(const char* key, Json\u0026amp;\u0026amp; v); Json\u0026amp; add_member(const char* key, Json\u0026amp; v); Add a key-value pair to a Json of object type (non-object Json automatically becomes object after calling this method).\nThis method will reserve the order in which keys were added, and keys may appear repeatedly.\nThe parameter key is a C string ending in '\\0', and the parameter v is the value.\nv is moved and becomes null after this operation.\nNOTE: for performance reasons, it is required that the key cannot contain double quotes.\nExample\nco::Json r; r.add_member(\u0026#34;a\u0026#34;, 1); // r -\u0026gt; {\u0026#34;a\u0026#34;:1} r.add_member(\u0026#34;d\u0026#34;, 3.3); // r -\u0026gt; {\u0026#34;a\u0026#34;:1, \u0026#34;d\u0026#34;:3.3} r.add_member(\u0026#34;s\u0026#34;, \u0026#34;xx\u0026#34;); // r -\u0026gt; {\u0026#34;a\u0026#34;:1, \u0026#34;d\u0026#34;:3.3, \u0026#34;s\u0026#34;:\u0026#34;xx\u0026#34;} co::Json x; x.add_member(\u0026#34;xx\u0026#34;, r); // r -\u0026gt; null r.add_member(\u0026#34;o\u0026#34;, co::Json().add_member(\u0026#34;x\u0026#34;, 3)); // r -\u0026gt; {\u0026#34;o\u0026#34;:{\u0026#34;x\u0026#34;:3}} co::Json().add_member(\u0026#34;o\u0026#34;, 1).add_member(\u0026#34;k\u0026#34;, 2); // -\u0026gt; {\u0026#34;o\u0026#34;:1,\u0026#34;k\u0026#34;:2} #erase void erase(uint32 i); void erase(int i); void erase(const char* key); The first two, erase the ith element from an array. The third, erase the element by key from an object. #push_back Json\u0026amp; push_back(Json\u0026amp;\u0026amp; v); Json\u0026amp; push_back(Json\u0026amp; v); Add elements to an array (non-array Json automatically becomes an array after calling this method).\nv is moved and becomes null after this operation.\nExample\nco::Json r; r.push_back(1); // r -\u0026gt; [1] r.push_back(3.3); // r -\u0026gt; [1, 3.3] r.push_back(\u0026#34;xx\u0026#34;); // r -\u0026gt; [1, 3.3, \u0026#34;xx\u0026#34;] co::Json x; x.push_back(r); // r -\u0026gt; null, x -\u0026gt; [[1, 3.3, \u0026#34;xx\u0026#34;]] r.push_back(co::Json().push_back(1).push_back(2)); // r -\u0026gt; [[1,2]] #remove void remove(uint32 i); void remove(int i); void remove(const char* key); The first two, remove the ith element from an array. The third, remove the element by key from an object. The last element will be moved to the position where the element was removed. #reset void reset(); Reset the Json object to null. #swap void swap(Json\u0026amp; v) noexcept; void swap(Json\u0026amp;\u0026amp; v) noexcept; Swap the contents of two Json objects. #——————————— #operator[] Json\u0026amp; operator[](uint32 i) const; Json\u0026amp; operator[](int i) const; Json\u0026amp; operator[](const char* key) const; Overload operator[], get the elements in the Json by index or key.\n1-2, for array type, get the i-th element of the array object, i must be within the size range of the array.\n3, for object type, when the key does not exist, a null object will be inserted into the Json.\nIn general, it is recommended to replace this operation with the read-only get() method whenever possible.\nExample\nco::Json r = { { \u0026#34;a\u0026#34;, 7 }, { \u0026#34;x\u0026#34;, { 1, 2, 3 } }, }; r[\u0026#34;a\u0026#34;].as_int(); // 7 r[\u0026#34;x\u0026#34;][0].as_int(); // 1 #has_member bool has_member(const char* key) const; Determine whether there is an element corresponding to key in Json.\nReturn false if the Json calling this method is not object type.\nExample\nco::Json r = {{\u0026#34;a\u0026#34;, 1}}; r.has_member(\u0026#34;a\u0026#34;); // true r.has_member(\u0026#34;x\u0026#34;); // false #size uint32 size() const; If Json is object or array, this method returns the number of elements.\nIf Json is string type, this method returns the length of the string.\nFor all other types, this method returns 0.\nExample\nco::Json r = { {\u0026#34;x\u0026#34;, 1}, {\u0026#34;s\u0026#34;, \u0026#34;hello\u0026#34;}, {\u0026#34;a\u0026#34;, {1, 2, 3}}, }; r.size(); // 3 r[\u0026#34;x\u0026#34;].size(); // 0 r[\u0026#34;s\u0026#34;].size(); // 5 r[\u0026#34;a\u0026#34;].size(); // 3 #empty bool empty() const; Check whether the Json is empty, which is equal to size() == 0. #string_size uint32 string_size() const; Return the length of the string type. Return 0 if the Json calling this method is not string type. #array_size uint32 array_size() const; Return the number of elements of array type. Return 0 if the Json calling this method is not array type. #object_size uint32 object_size() const; Return the number of elements of object type. Return 0 if the Json calling this method is not object type. #——————————— #str fastream\u0026amp; str(fastream\u0026amp; s, int mdp=16) const; fastring\u0026amp; str(fastring\u0026amp; s, int mdp=16) const; fastring str(int mdp=16) const; Convert Json to a string. The 1st version appends the JSON string to a fastream, and the return value is the same as the parameter s. The 2nd version appends the JSON string to a fastring, and the return value is the same as the parameter s. The 3rd version returns a JSON string. The parameter mdp is short for max decimal places, which means the maximum number of decimal places for float point numbers. #pretty fastream\u0026amp; pretty(fastream\u0026amp; s, int mdp=16) const; fastring\u0026amp; pretty(fastring\u0026amp; s, int mdp=16) const; fastring pretty(int mdp=16) const; Like the str(), but convert Json to a more beautiful JSON string. #dbg fastream\u0026amp; dbg(fastream\u0026amp; s, int mdp=16) const; fastring\u0026amp; dbg(fastring\u0026amp; s, int mdp=16) const; fastring dbg(int mdp=16) const; Convert Json to a debug string, like str(), but will truncate string type to the first 32 bytes if its length exceeds 512 bytes. This method is generally used to print logs. In some cases, the Json object may contain a long string, such as the base64 encoding of a picture. At such cases, use dbg() instead of str() to avoid printing too many significant logs. #parse_from bool parse_from(const char* s, size_t n); bool parse_from(const char* s); bool parse_from(const fastring\u0026amp; s); bool parse_from(const std::string\u0026amp; s); Parse Json from a JSON string. In the first version, s is not required to end with '\\0'. When the parsing is successful, it returns true, otherwise it returns false. When the parsing fails, the calling Json becomes null. #Example co::Json r = { { \u0026#34;a\u0026#34;, {1,2,3} } }; fastring s = r.str(); // s -\u0026gt; {\u0026#34;a\u0026#34;:[1,2,3]} fastring p = r.pretty(); LOG \u0026lt;\u0026lt; r.dbg(); // print json debug string LOG \u0026lt;\u0026lt; r; // the same as above, but is more efficient co::Json x; x.parse_from(s); x.parse_from(p); co::Json v = json::parse(s); #——————————— #begin iterator begin() const; Returns the beginning iterator. The Json calling this method must be array, object or null. When the Json is empty, the return value is equal to end(). If the Json calling this method is not array or object type, the return value is equal to end(). #end const iterator::End\u0026amp; end() const; Returns a fake end iterator. The return value is actually not an iterator object, but a iterator can be compared with it. If an iterator is equal to end(), it means that there is no more element. #iterator #operator== bool operator==(const End\u0026amp;) const; Determine whether a iterator is equal to End, End is the fake end iterator. #operator!= bool operator!=(const End\u0026amp;) const; Determine if a iterator is not equal to End, End is the fake end iterator. #operator++ iterator\u0026amp; operator++(); The prefix operator++. #operator* Json\u0026amp; operator*() const; Overload operator*, this method only applies to iterator of array type. When Json is an array, the iterator points to the elements in the array. #key const char* key() const; This method only applies to iterator of object type. When Json is an object, the iterator points to the key-value pair in the object, and this method returns the key. #value Json\u0026amp; value() const; This method only applies to iterator of object type. When Json is an object, the iterator points to the key-value pair in the object, and this method returns the value. #Traversing the Json co.json supports traversing a Json of type array or object by iterator:\n// {\u0026#34;i\u0026#34;:7, \u0026#34;s\u0026#34;:\u0026#34;xx\u0026#34;, \u0026#34;a\u0026#34;:[123, true, \u0026#34;nice\u0026#34;]} co::Json r = { {\u0026#34;i\u0026#34;, 7}, {\u0026#34;s\u0026#34;, \u0026#34;xx\u0026#34;}, {\u0026#34;a\u0026#34;, {1, 2, 3}}, } // object for (auto it = r.begin(); it != r.end(); ++it) { LOG \u0026lt;\u0026lt; it.key() \u0026lt;\u0026lt; \u0026#34;: \u0026#34; \u0026lt;\u0026lt; it.value(); } // array co::Json\u0026amp; a = r[\u0026#34;a\u0026#34;]; for (auto it = a.begin(); it != a.end(); ++it) { LOG \u0026lt;\u0026lt; (*it); } #Performance tips Some users may add members in the following way:\nco::Json r; r[\u0026#34;a\u0026#34;] = 1; r[\u0026#34;s\u0026#34;] = \u0026#34;hello world\u0026#34;; Although the above code works, the efficiency may be not so good. The operator[] will first look up the key, which may be slow. It is generally recommended to use add_member() instead:\nco::Json r; r.add_member(\u0026#34;a\u0026#34;, 1); r.add_member(\u0026#34;s\u0026#34;, \u0026#34;hello world\u0026#34;); Or construct a Json like this:\nco::Json r = { {\u0026#34;a\u0026#34;, 1}, {\u0026#34;s\u0026#34;, \u0026#34;hello world\u0026#34;}, }; For read-only operations, it is recommended to replace operator[] with get(), which has no side effects.\nco::Json r = {{\u0026#34;a\u0026#34;, 1}}; r.get(\u0026#34;a\u0026#34;).as_int(); // 1 "},{"id":37,"href":"/en/co/concurrency/coroutine/conf/","title":"Config","section":"Coroutine","content":"#Config Coost uses co.flag to define config items for the coroutine module. Please refer to co.flag documents for detailed usage.\n#co_hook_log DEF_bool(co_hook_log, false, \u0026#34;\u0026gt;\u0026gt;#1 print log for API hooks\u0026#34;); Print API hook related logs, default is false. Coost v3.0.1 renamed hook_log to co_hook_log. #co_sched_log DEF_bool(co_sched_log, false, \u0026#34;\u0026gt;\u0026gt;#1 print logs for coroutine schedulers\u0026#34;); Print debug logs for coroutine scheduling, default is false. Coost v3.0.1 renamed co_debug_log to co_sched_log. #co_sched_num DEF_uint32(co_sched_num, os::cpunum(), \u0026#34;\u0026gt;\u0026gt;#1 number of coroutine schedulers\u0026#34;); The number of coroutine scheduling threads, which defaults to the number of system CPU cores. In the current implementation, its maximum value is also the number of system CPU cores. #co_stack_num DEF_uint32(co_stack_num, 8, \u0026#34;\u0026gt;\u0026gt;#1 number of stacks per scheduler, must be power of 2\u0026#34;); Added in v3.0.1, the number of shared coroutine stacks for each scheduler, must be power of 2, and is 8 by default. #co_stack_size DEF_uint32(co_stack_size, 1024 * 1024, \u0026#34;\u0026gt;\u0026gt;#1 size of the stack shared by coroutines\u0026#34;); Coroutine stack size, default is 1M. "},{"id":38,"href":"/en/co/other/time/","title":"Time","section":"others","content":"include: co/time.h.\n#epoch time The EPOCH is a specific time 1970-01-01 00:00:00 UTC, and the epoch time is the time since the EPOCH.\n#epoch::ms int64 ms(); Return milliseconds since EPOCH. #epoch::us int64 us(); Return microseconds since EPOCH. #monotonic time Monotonic time is a monotonic increasing time, it is implemented as the time since last reboot of system on most platforms. It is generally used for timing and is more stable than system time.\n#now::ms int64 ms(); Returns a monotonically increasing timestamp in milliseconds. On mac platform, if the system does not support CLOCK_MONOTONIC, epoch::ms() will be used instead. #now::us int64 us(); Returns a monotonically increasing timestamp in microseconds.\nOn mac platform, if the system does not support CLOCK_MONOTONIC, epoch::us() will be used instead.\nExample\nint64 beg = now::us(); int64 end = now::us(); LOG \u0026lt;\u0026lt; \u0026#34;time used: \u0026#34;\u0026lt;\u0026lt; (end-beg) \u0026lt;\u0026lt;\u0026#34; us\u0026#34;; #Time string (now::str) // fm: time output format fastring str(const char* fm=\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;); This function returns the string form of the current system time in the specified format. It is implemented based on strftime.\nExample\nfastring s = now::str(); // \u0026#34;2021-07-07 17:07:07\u0026#34; fastring s = now::str(\u0026#34;%Y\u0026#34;); // \u0026#34;2021\u0026#34; #sleep #sleep::ms void ms(uint32 n); Sleep for n milliseconds. #sleep::sec void sec(uint32 n); Sleep for n seconds.\nExample\nsleep::ms(10); // sleep for 10 milliseconds sleep::sec(1); // sleep for 1 second #co::Timer co::Timer is a simple timer based on monotonic time.\nTimer was added to namespace co since v3.0.1. #constructor Timer(); Set the start time of the timer, and start timing when the object is created. #ms int64 ms() const; Return milliseconds since start of the timing. #us int64 us() const; Return microseconds since start of the timing. #restart void restart(); Restart the timer. #Example co::Timer t; sleep::ms(10); int64 us = t.us(); t.restart(); sleep::ms(20); int64 ms = t.ms(); "},{"id":39,"href":"/en/co/other/tasked/","title":"Timed task scheduler","section":"others","content":"include: co/tasked.h.\n#co::Tasked The class Tasked implements a simple timed task scheduler. All tasks are scheduled internally by a single thread, but tasks can be added from any thread. When a task is blocked, it will affect all subsequent tasks. Therefore, it is not recommended to use it to schedule tasks that may block for a long time.\nTasked was added to namespace co since v3.0.1. #Constructor 1. Tasked(); 2. Tasked(Tasked\u0026amp;\u0026amp; t); 1, the default constructor. When the object is created, the scheduling thread will start immediately. 2, the move constructor. #Destructor ~Tasked(); The destructor, stop the task scheduling thread. #F typedef std::function\u0026lt;void()\u0026gt; F; The task type. #run_at void run_at(F\u0026amp;\u0026amp; f, int hour, int minute=0, int second=0); void run_at(const F\u0026amp; f, int hour, int minute=0, int second=0); Add a task to run at the specified time, f will run once at time of hour:minute:second. hour must be an integer between 0-23, minute and second must be an integer between 0-59, and the default is 0. #run_daily void run_daily(F\u0026amp;\u0026amp; f, int hour=0, int minute=0, int second=0); void run_daily(const F\u0026amp; f, int hour=0, int minute=0, int second=0); Add a periodic task that runs at a specified time every day, f will run once every day at the time of hour:minute:second. hour must be an integer between 0-23, the default is 0, minute and second are integers between 0-59, and the default is 0. #run_every void run_every(F\u0026amp;\u0026amp; f, int n); void run_every(const F\u0026amp; f, int n); Add a periodic task that runs every n seconds. #run_in void run_in(F\u0026amp;\u0026amp; f, int n); void run_in(const F\u0026amp; f, int n); Add a task that runs once in n seconds. #stop void stop(); Stop the task scheduling thread, this method will be automatically called in the destructor. It is safe to call this method multiple times. #Code example co::Tasked s; // create and start the scheduler s.run_in(f, 3); // run f 3 seconds later s.run_every(std::bind(f, 0), 3); // run f every 3 seconds s.run_at(f, 23); // run f once at 23:00:00 s.run_daily(f); // run f at 00:00:00 every day s.run_daily(f, 23); // run f at 23:00:00 every day s.run_daily(f, 23, 30); // run f at 23:30:00 every day s.stop(); // stop the scheduler "},{"id":40,"href":"/en/co/other/path/","title":"Path","section":"others","content":"include: co/path.h.\n#path This library is ported from golang\u0026rsquo;s path package, the path separator must be / .\n#path::clean fastring clean(const char* s, size_t n); fastring clean(const char* s); fastring clean(const fastring\u0026amp; s); Return the shortest equivalent form of the path, the consecutive separators in the path will be removed.\nExample\npath::clean(\u0026#34;\u0026#34;); // \u0026#34;.\u0026#34; path::clean(\u0026#34;./x//y/\u0026#34;); // \u0026#34;x/y\u0026#34; path::clean(\u0026#34;./x/..\u0026#34;); // \u0026#34;.\u0026#34; path::clean(\u0026#34;./x/../..\u0026#34;); // \u0026#34;..\u0026#34; #path::join template\u0026lt;typename ...S\u0026gt; inline fastring join(S\u0026amp;\u0026amp;... s); Concatenate any number of strings into a complete path, the returned result is cleaned by path::clean.\nEmpty strings in the parameters will be ignored.\nExample\npath::join(\u0026#34;\u0026#34;, \u0026#34;\u0026#34;); // \u0026#34;\u0026#34; path::join(\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;, \u0026#34;z\u0026#34;); // \u0026#34;x/y/z\u0026#34; path::join(\u0026#34;/x/\u0026#34;, \u0026#34;y\u0026#34;); // \u0026#34;/x/y\u0026#34; #path::split std::pair\u0026lt;fastring, fastring\u0026gt; split(const char* s, size_t n); std::pair\u0026lt;fastring, fastring\u0026gt; split(const char* s); std::pair\u0026lt;fastring, fastring\u0026gt; split(const fastring\u0026amp; s); Divide the path into two parts, dir and file. If the path does not contain a separator, the dir part will be empty.\nThe returned result satisfies the property path = dir + file.\nExample\npath::split(\u0026#34;/\u0026#34;); // -\u0026gt; {\u0026#34;/\u0026#34;, \u0026#34;\u0026#34;} path::split(\u0026#34;/a\u0026#34;); // -\u0026gt; {\u0026#34;/\u0026#34;, \u0026#34;a\u0026#34;} path::split(\u0026#34;/a/\u0026#34;); // -\u0026gt; { \u0026#34;/a/\u0026#34;, \u0026#34;\u0026#34; } path::split(\u0026#34;/a/b\u0026#34;); // -\u0026gt; {\u0026#34;/a/\u0026#34;, \u0026#34;b\u0026#34;} #path::dir fastring dir(const char* s, size_t n); fastring dir(const char* s); fastring dir(const fastring\u0026amp; s); Return the directory part of the path, the return value is a cleaned path by path::clean.\nExample\npath::dir(\u0026#34;a\u0026#34;); // \u0026#34;.\u0026#34; path::dir(\u0026#34;a/\u0026#34;); // \u0026#34;a\u0026#34; path::dir(\u0026#34;/\u0026#34;); // \u0026#34;/\u0026#34; path::dir(\u0026#34;/a\u0026#34;); // \u0026#34;/\u0026#34;; path::dir(\u0026#34;/a/\u0026#34;); // \u0026#34;/a\u0026#34;; #path::base fastring base(const char* s, size_t n); fastring base(const char* s); fastring base(const fastring\u0026amp; s); Return the last element of the path.\nIf s is empty, return \u0026ldquo;.\u0026rdquo;.\nIf s consists entirely of slashes, return \u0026ldquo;/\u0026rdquo;.\nIn other cases, remove trailing slashes of s before extracting the last element.\nExample\npath::base(\u0026#34;\u0026#34;); // \u0026#34;.\u0026#34; path::base(\u0026#34;/\u0026#34;); // \u0026#34;/\u0026#34; path::base(\u0026#34;/a/\u0026#34;); // \u0026#34;a\u0026#34; path::base(\u0026#34;/a\u0026#34;); // \u0026#34;a\u0026#34; path::base(\u0026#34;/a/b\u0026#34;); // \u0026#34;b\u0026#34; #path::ext fastring ext(const char* s, size_t n); fastring ext(const char* s); fastring ext(const fastring\u0026amp; s); Return the file name extension of the path.\nExample\npath::ext(\u0026#34;/a.c\u0026#34;); // \u0026#34;.c\u0026#34; path::ext(\u0026#34;a/b\u0026#34;); // \u0026#34;\u0026#34; path::ext(\u0026#34;/a.c/\u0026#34;); // \u0026#34;\u0026#34; path::ext(\u0026#34;a.\u0026#34;); // \u0026#34;.\u0026#34; "},{"id":41,"href":"/en/co/other/fs/","title":"File System","section":"others","content":"include: co/fs.h.\nThe fs module implements common file system operations. It is recommended to use / uniformly as the path separator on different platforms.\n#Metadata operations #fs::exists bool exists(const char* path); bool exists(const fastring\u0026amp; path); bool exists(const std::string\u0026amp; path); Check whether the file exists, the parameter path is the path of a file or directory. #fs::fsize int64 fsize(const char* path); int64 fsize(const fastring\u0026amp; path); int64 fsize(const std::string\u0026amp; path); Get the file size. -1 will be returned if the file does not exist. #fs::isdir bool isdir(const char* path); bool isdir(const fastring\u0026amp; path); bool isdir(const std::string\u0026amp; path); Check whether the file is a directory, if path exists and is a directory, it returns true, otherwise it returns false. #fs::mtime int64 mtime(const char* path); int64 mtime(const fastring\u0026amp; path); int64 mtime(const std::string\u0026amp; path); Get the modification time of the file, return -1 when the file does not exist. #fs::mkdir bool mkdir(const char* path, bool p=false); bool mkdir(const fastring\u0026amp; path, bool p=false); bool mkdir(const std::string\u0026amp; path, bool p=false); Create a directory, the parameter p indicates whether to create the entire path. The parameter p is false by default, and the directory will be created only when the parent directory exists. When p is true, it is equivalent to mkdir -p, and the parent directory will be created first if it does not exist. #fs::mv bool mv(const char* from, const char* to); bool mv(const fastring\u0026amp; from, const fastring\u0026amp; to); bool mv(const std::string\u0026amp; from, const std::string\u0026amp; to); Added in v3.0.2, move or rename a file or directory, the behavior is similar to the mv command in Linux. When to exists and is a directory, from will be moved into directory to. If the target is of the same type as from (both are directories or files), and the target is not a non-empty directory, the target will be overwritten. // Assume directory d already exists fs::mv(\u0026#34;xx.txt\u0026#34;, \u0026#34;xx.log\u0026#34;); // xx.txt -\u0026gt; xx.log fs::mv(\u0026#34;xx.txt\u0026#34;, \u0026#34;d\u0026#34;); // xx.txt -\u0026gt; d/xx.txt #fs::remove bool remove(const char* path, bool r=false); bool remove(const fastring\u0026amp; path, bool r=false); bool remove(const std::string\u0026amp; path, bool r=false); Delete a file or directory. The parameter r is false by default, which only deletes file or empty directory; when r is true, it is equivalent to rm -r, which can delete non-empty directory. #fs::rename bool rename(const char* from, const char* to); bool rename(const fastring\u0026amp; from, const fastring\u0026amp; to); bool rename(const std::string\u0026amp; from, const std::string\u0026amp; to); Deprecated in v3.0.2. Use fs::mv instead. #fs::symlink bool symlink(const char* dst, const char* lnk); bool symlink(const fastring\u0026amp; dst, const fastring\u0026amp; lnk); bool symlink(const std::string\u0026amp; dst, const std::string\u0026amp; lnk); Create a soft link, the parameter dst is the path of the target file or directory, and the parameter lnk is the path of the soft link. This function first calls fs::remove(lnk) to delete the old soft link, and then create a new one. On windows, this function requires admin permission. #Code example bool x = fs::exists(path); // Determine whether the file exists bool x = fs::isdir(path); // Determine whether the file is a directory int64 x = fs::mtime(path); // Get the modification time of the file int64 x = fs::fsize(path); // Get the size of the file fs::mkdir(\u0026#34;a/b\u0026#34;); // mkdir a/b fs::mkdir(\u0026#34;a/b\u0026#34;, true); // mkdir -p a/b fs::remove(\u0026#34;x/x.txt\u0026#34;); // rm x/x.txt fs::remove(\u0026#34;a/b\u0026#34;); // rmdir a/b fs::remove(\u0026#34;a/b\u0026#34;, true); // rm -rf a/b fs::rename(\u0026#34;a/b\u0026#34;, \u0026#34;a/c\u0026#34;); // mv a/b a/c fs::symlink(\u0026#34;/usr\u0026#34;, \u0026#34;x\u0026#34;); // ln -s /usr x #fs::file The fs::file class implements the basic read and write operations of files. Unlike fread and fwrite, it has no cache, but reads and writes files directly.\n#file::file 1. file(); 2. file(file\u0026amp;\u0026amp; f); 3. file(const char* path, char mode); 4. file(const fastring\u0026amp; path, char mode); 5. file(const std::string\u0026amp; path, char mode); 1, the default constructor, which creates an empty file object without opening a file. 2, the move constructor. 3-5, open the file specified by path, and the parameter mode is the open mode. mode is one of 'r', 'w', 'a', 'm' or '+', r for read, w for write, a for append, m for modify, + for both read and write. When mode is 'r', the file must exist, otherwise the file is not opened. When mode is 'w', a new file will be created if it does not exist, and the file will be truncated if it already exists. When mode is 'a', 'm' or '+', a new file will be created when it does not exist, and the file will not be truncated if it already exists. '+' was added since v3.0. In this mode, read and write share the file pointer, therefore, you may need call the seek() method to set the offset before the read or write operation. #file::~file ~file(); Destructor, close the previously opened file and release related resources. #file::close void close(); Close the file, this method will be automatically called in the destructor. It is safe to call this method multiple times. #file::exists bool exists() const; Determine whether the file exists. The file may be deleted by other processes. This method can be used to determine whether the previously opened file still exists. #file::open bool open(const char* path, char mode); bool open(const fastring\u0026amp; path, char mode); bool open(const std::string\u0026amp; path, char mode); This method opens the specified file, mode is the open mode, see the description in Constructor. This method will close the previously opened file before opening a new file. #file::operator bool explicit operator bool() const; Convert fs::file to bool type, return true when the file is successfully opened, otherwise return false. #file::operator! bool operator!() const; It returns true when the file is not opened, otherwise it returns false. #file::path const char* path() const; Return path of the file. If the file object is not associated with any file, return empty string \u0026quot;\u0026quot;. #file::read 1. size_t read(void* buf, size_t n); 2. fastring read(size_t n); 1, reads data into the specified buffer, n is the number of bytes to be read, and returns the number of bytes actually read. 2, similar to the first one, but returns a fastring, where n is the number of bytes to be read. The actual bytes read may be less than n, when it reaches the end of the file or an error occurs. #file::seek void seek(int64 off, int whence=seek_beg); Set the current position of the file pointer, the parameter off is the offset position, and the parameter whence is the starting position, which can be one of file::seek_beg, file::seek_cur, file::seek_end. This method is not valid for files opened with mode 'a'. #file::size int64 size() const; Returns size of the file. When the file is not opened, return -1. #file::write 1. size_t write(const void* s, size_t n); 2. size_t write(const char* s); 3. size_t write(const fastring\u0026amp; s); 4. size_t write(const std::string\u0026amp; s); 5. size_t write(char c); 1, writes n bytes. 2-4, write a string. 5, writes a single character. This method returns the number of bytes actually written. When the disk space is insufficient or other error occurs, the return value may be less than n. This method has already handled EINTR error internally. #Example fs::file f; // empty file fs::file f(\u0026#34;xx\u0026#34;, \u0026#39;w\u0026#39;); // write mode f.open(\u0026#34;xx\u0026#34;, \u0026#39;m\u0026#39;); // reopen with modify mode f.open(\u0026#34;xx\u0026#34;, \u0026#39;r\u0026#39;); // read mode if (f) f.read(buf, 512); // read at most 512 bytes fastring s = f.read(32); // read at most 32 bytes and return fastring f.open(\u0026#34;xx\u0026#34;, \u0026#39;a\u0026#39;); // append mode if(f) f.write(buf, 32); // write 32 bytes f.write(\u0026#34;hello\u0026#34;); // write a C string f.write(\u0026#39;c\u0026#39;); // write a single character f.open(\u0026#34;xx\u0026#34;, \u0026#39;+\u0026#39;); // read/write mode f.seek(0); // seek to beginning before write f.write(\u0026#34;hello\u0026#34;); f.seek(0); // seek to beginning before read f.read(buf, 8); f.close(); // close the file #fs::fstream fs::file does not support caching, and the performance of writing small files is poor. So coost implements the fs::fstream class which supports caching, it is designed for writing files and does not support read operations.\n#fstream::fstream 1. fstream(); 2. fstream(fstream\u0026amp;\u0026amp; fs); 3. explicit fstream(size_t cap); 4. fstream(const char* path, char mode, size_t cap=8192); 5. fstream(const fastring\u0026amp; path, char mode, size_t cap=8192); 6. fstream(const std::string\u0026amp; path, char mode, size_t cap=8192); 1, the default constructor, and the internal cache size is 8k. 2, the move constructor. 3, specify the size of the cache to cap. 4-6, open the specified file, cap is the cache size, and the default is 8k. The parameter mode is one of 'w' or 'a', and the read mode is not supported. When mode is 'w', the file is automatically created when it does not exist, and the file data is cleared if it already exists. When mode is 'a', the file will be created automatically if it does not exist, and the file data will not be cleared if it already exists. #fstream::~fstream ~fstream(); Destructor, close the file, release related resources. #fstream::append fstream\u0026amp; append(const void* s, size_t n); Append n bytes to the file. #fstream::close void close(); Close the file, this method will be automatically called in the destructor. It is safe to call this method multiple times. #fstream::flush void flush(); Write data in the cache to the file. #fstream::open bool open(const char* path, char mode); bool open(const fastring\u0026amp; path, char mode); bool open(const std::string\u0026amp; path, char mode); Open the specified file. This method will close the previously opened file before opening a new file. #fstream::operator bool explicit operator bool() const; Convert fs::fstream to bool type, return true when the file is successfully opened, otherwise return false. #fstream::operator! bool operator!() const; It returns true when the file is not opened, otherwise it returns false. #fstream::operator\u0026laquo; 1. fstream\u0026amp; operator\u0026lt;\u0026lt;(const char* s); 2. fstream\u0026amp; operator\u0026lt;\u0026lt;(const fastring\u0026amp; s); 3. fstream\u0026amp; operator\u0026lt;\u0026lt;(const std::string\u0026amp; s); 4. fstream\u0026amp; operator\u0026lt;\u0026lt;(const fastream\u0026amp; s); 5. template\u0026lt;typename T\u0026gt; fstream\u0026amp; operator\u0026lt;\u0026lt;(T v); 1-3, the parameter s is a string type. 4, the parameter s is a fastream. 5, T can be any built-in type, such as bool, char, int, double, etc. #fstream::reserve void reserve(size_t n); Adjust the cache capacity, the parameter n is the capacity. If n is less than the previous capacity, the capacity remains unchanged. #Code example fs::fstream s; // cache size: 8k fs::fstream s(4096); // cache size: 4k fs::fstream s(\u0026#34;path\u0026#34;,\u0026#39;a\u0026#39;); // append mode, cache size: 8k fs::fstream s(\u0026#34;path\u0026#34;,\u0026#39;w\u0026#39;, 4096); // write mode, cache size: 4k s.reserve(8192); // make cache size at least 8k s.open(\u0026#34;path\u0026#34;,\u0026#39;a\u0026#39;); // open with append mode if (s) s \u0026lt;\u0026lt; \u0026#34;hello world\u0026#34; \u0026lt;\u0026lt; 23; // operator\u0026lt;\u0026lt; s.append(\u0026#34;hello\u0026#34;, 5); // append s.flush(); // flush data in cache to file s.close(); // close the file #fs::dir The fs::dir class is added in v3.0.1, which is used to read directories.\n#dir::dir 1. dir(); 2. dir(dir\u0026amp;\u0026amp; d); 3. explicit dir(const char* path); 4. explicit dir(const fasting\u0026amp; path); 5. explicit dir(const std::string\u0026amp; path); 1, the default constructor, creates an empty dir object and does not open any directory. 2, the move constructor. 3-5, open the directory specified by path. #dir::~dir ~dir(); Destructor, close the opened directory and release related resources. #dir::all co::vector\u0026lt;fastring\u0026gt; all() const; Read all items in the directory, . and .. will be ignored. #dir::begin iterator begin() const; Returns the begin iterator. #dir::close void close(); Close the directory, it is automatically called in the destructor. It is safe to call this method multiple times. #dir::end iterator end() const; Returns the end iterator. #dir::iterator The dir::iterator class is used to iterate through the directories opened in the dir object.\n#operator* fasting operator*() const; Returns the file or directory name corresponding to the iterator. #operator++ iterator\u0026amp; operator++(); Overload prefixed ++ operations. #operator== bool operator==(const iterator\u0026amp; it) const; Tests whether two iterators are equal. #operator!= bool operator!=(const iterator\u0026amp; it) const; Tests whether two iterators are not equal. #dir::open bool open(const char* path); bool open(const fasting\u0026amp; path); bool open(const std::string\u0026amp; path); Open the directory specified by path, and close the previously opened directory before opening a new directory. #dir::path const char* path() const; Returns path of the directory. If the dir object is not associated with any directory, return empty string \u0026quot;\u0026quot; . #code example fs::dir d(\u0026#34;xx\u0026#34;); auto v = d.all(); // read all subitems d.open(\u0026#34;abc\u0026#34;); for (auto it = d.begin(); it != d.end(); ++it) { cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; } "},{"id":42,"href":"/en/co/other/os/","title":"Operating System","section":"others","content":"include: co/os.h.\n#os #os::cpunum int cpunum(); Returns the number of system CPU cores. #os::cwd fastring cwd(); Returns path of the current working directory. On windows, \\ in the results will be converted to /. #os::daemon void daemon(); Put the current process to run in the background, for linux only. #os::env 1. fastring env(const char* name); 2. bool env(const char* name, const char* value); 1, get value of the environment variable. 2, added in v2.0.2, set value of the environment variable, return true on success, otherwise false. #os::exename fastring exename(); Returns name of the current process (without path). #os::exepath fastring exepath(); Returns the full path of the current process. On windows, \\ in the results will be converted to /. #os::homedir fastring homedir(); Returns path of the home directory of the current user. On windows, \\ in the results will be converted to /. #os::pid int pid(); Returns the id of the current process. #os::signal typedef void (*sig_handler_t)(int); sig_handler_t signal(int sig, sig_handler_t handler, int flag=0); Set a handler for a signal, the parameter sig is the signal value, and the parameter flag is the combination of SA_RESTART, SA_ONSTACK or other options.\nThe parameter flag is only applicable to linux/mac platforms.\nThis function returns the old signal handler.\nExample\nvoid f(int); os::signal(SIGINT, f); // user defined handler os::signal(SIGABRT, SIG_DFL); // default handler os::signal(SIGPIPE, SIG_IGN); // ignore SIGPIPE "},{"id":43,"href":"/en/co/build/","title":"Compiling","section":"Documents","content":"#Compiler requirements The compilers required are as follows:\nLinux: gcc 4.8+ Mac: clang 3.3+ Windows: vs2015+ #xmake Coost recommends using xmake as the build tool.\n#Install xmake For Windows, mac and debian/ubuntu, you can go directly to the release page of xmake to get the installation package. For other systems, please refer to xmake\u0026rsquo;s Installation instructions.\n#Build Run commands below in the root directory of co to build libco and other projects:\nxmake -a # build all projects (libco, gen, test, unitest) To enable HTTP and SSL features, build with the following commands:\nxmake f --with_libcurl=true --with_openssl=true xmake -a Xmake may install libcurl and openssl from the network, which may be slow.\n-a in the command line means to build all projects in coost. If -a is not added, only libco will be built by default. In addition, users may use -v or -vD to print more detailed compiling information:\nxmake -v -a #Compiling options Xmake provides the xmake f command to configure compiling options. Note that multiple options must be set in a single xmake f command.\n#Build debug version of libco xmake f -m debug xmake -v #Build dynamic library xmake f -k shared xmake -v #Build 32-bit libco Windows xmake f -a x86 xmake -v Linux xmake f -a i386 xmake -v The -a in xmake f command means arch. The arch supported by different platforms may be different. Run xmake f --help to see the details.\n#set vs_runtime on Windows On Windows, CO uses the MT runtime library by default, and users can use xmake f to configure it:\nxmake f --vs_runtime=MD xmake -v #Android and IOS support Coost can also be built on Android and IOS platforms, see Github Actions for details. Coost has not been tested on Android and IOS yet.\nandroid xmake f -p android --ndk=/path/to/android-ndk-r21 xmake -v ios xmake f -p iphoneos xmake -v #Build and run unitest code co/unitest contains some unit test code, run the following commands to build and run the test program:\nxmake -b unitest # build unitest xmake r unitest -a # run all unit tests xmake r unitest -os # run unit test: os xmake r unitest -json # run unit test: json #Build and run test code co/test contains some test code, add xx.cc source file in the co/test directory or its subdirectories, and then run xmake -b xx in the root directory of CO to build it.\nxmake -b flag # compile test/flag.cc xmake -b log # compile test/log.cc xmake -b json # compile test/json.cc xmake -b rpc # compile test/rpc.cc xmake r flag -xz # test flag library xmake r log # test log library xmake r log -cout # also log to terminal xmake r log -perf # test performance of log library xmake r json # test json xmake r rpc # start rpc server xmake r rpc -c # start rpc client #Build and use gen xmake -b gen cp gen /usr/local/bin/ gen hello_world.proto #Install libco After building libco, you can use the xmake install command to install libco to the specified directory:\nxmake install -o pkg # install to pkg xmake i -o pkg # same as above xmake i -o /usr/local # install to /usr/local #Install libco from xmake repo xrepo install -f \u0026#34;openssl=true,libcurl=true\u0026#34; coost #cmake izhengfan helped to provide the cmakefile:\nOnly build libco by default. The library files are in build/lib directory, and the executable files are in build/bin directory. You can use BUILD_ALL to build all projects. You can use CMAKE_INSTALL_PREFIX to specify the installation directory. #Build libco by default mkdir build \u0026amp;\u0026amp; cd build cmake .. make -j8 #Build all projects mkdir build \u0026amp;\u0026amp; cd build cmake .. -DBUILD_ALL=ON -DCMAKE_INSTALL_PREFIX=/usr/local make -j8 make install #Enable HTTP and SSL features To use HTTP or SSL features, libcurl, zlib, and openssl 1.1.0 or above must be installed.\nmkdir build \u0026amp;\u0026amp; cd build cmake .. -DBUILD_ALL=ON -DWITH_LIBCURL=ON -DWITH_OPENSSL=ON make -j8 "}]